From 94d2ed820e7536d8fa7070d11729cce697fbb0b3 Mon Sep 17 00:00:00 2001 From: notmd Date: Wed, 25 Jan 2023 15:04:39 +0700 Subject: [PATCH 1/5] Refactor `OasstApiClient` --- website/src/components/UserTable.tsx | 3 +- website/src/lib/oasst_api_client.ts | 215 +++++++++------------------ website/src/types/Users.ts | 16 ++ 3 files changed, 87 insertions(+), 147 deletions(-) diff --git a/website/src/components/UserTable.tsx b/website/src/components/UserTable.tsx index 71d4fe44..57a96c95 100644 --- a/website/src/components/UserTable.tsx +++ b/website/src/components/UserTable.tsx @@ -4,8 +4,7 @@ import { Pencil } from "lucide-react"; import Link from "next/link"; import { memo, useState } from "react"; import { get } from "src/lib/api"; -import { FetchUsersResponse } from "src/lib/oasst_api_client"; -import type { User } from "src/types/Users"; +import type { FetchUsersResponse, User } from "src/types/Users"; import useSWR from "swr"; import { DataTable, DataTableColumnDef, FilterItem } from "./DataTable"; diff --git a/website/src/lib/oasst_api_client.ts b/website/src/lib/oasst_api_client.ts index 7c48a288..0b3ecf1d 100644 --- a/website/src/lib/oasst_api_client.ts +++ b/website/src/lib/oasst_api_client.ts @@ -1,36 +1,20 @@ import type { Message } from "src/types/Conversation"; import { LeaderboardReply, LeaderboardTimeFrame } from "src/types/Leaderboard"; import type { AvailableTasks } from "src/types/Task"; -import type { BackendUser, BackendUserCore, User } from "src/types/Users"; +import type { BackendUser, BackendUserCore, FetchUsersParams, FetchUsersResponse } from "src/types/Users"; export class OasstError { message: string; errorCode: number; httpStatusCode: number; - constructor(message: string, errorCode: number, httpStatusCode?: number) { + constructor(message: string, errorCode: number, httpStatusCode: number) { this.message = message; this.errorCode = errorCode; this.httpStatusCode = httpStatusCode; } } -export type FetchUsersParams = { - limit: number; - cursor?: string; - direction: "forward" | "back"; - searchDisplayName?: string; - sortKey?: "username" | "display_name"; -}; - -export type FetchUsersResponse = { - items: T[]; - next?: string; - prev?: string; - sort_key: "username" | "display_name"; - order: "asc" | "desc"; -}; - export class OasstApiClient { oasstApiUrl: string; oasstApiKey: string; @@ -39,88 +23,6 @@ export class OasstApiClient { this.oasstApiUrl = oasstApiUrl; this.oasstApiKey = oasstApiKey; } - - private async post(path: string, body: any): Promise { - const resp = await fetch(`${this.oasstApiUrl}${path}`, { - method: "POST", - headers: { - "X-API-Key": this.oasstApiKey, - "Content-Type": "application/json", - }, - body: JSON.stringify(body), - }); - - if (resp.status === 204) { - return null; - } - - if (resp.status >= 300) { - const errorText = await resp.text(); - let error: any; - try { - error = JSON.parse(errorText); - } catch (e) { - throw new OasstError(errorText, 0, resp.status); - } - throw new OasstError(error.message ?? error, error.error_code, resp.status); - } - - return await resp.json(); - } - - private async put(path: string): Promise { - const resp = await fetch(`${this.oasstApiUrl}${path}`, { - method: "PUT", - headers: { - "X-API-Key": this.oasstApiKey, - }, - }); - - if (resp.status === 204) { - return null; - } - - if (resp.status >= 300) { - const errorText = await resp.text(); - let error: any; - try { - error = JSON.parse(errorText); - } catch (e) { - throw new OasstError(errorText, 0, resp.status); - } - throw new OasstError(error.message ?? error, error.error_code, resp.status); - } - - return await resp.json(); - } - - private async get(path: string): Promise { - const resp = await fetch(`${this.oasstApiUrl}${path}`, { - method: "GET", - headers: { - "X-API-Key": this.oasstApiKey, - "Content-Type": "application/json", - }, - }); - - if (resp.status === 204) { - return null; - } - - if (resp.status >= 300) { - const errorText = await resp.text(); - let error: any; - try { - error = JSON.parse(errorText); - } catch (e) { - throw new OasstError(errorText, 0, resp.status); - } - throw new OasstError(error.message ?? error, error.error_code, resp.status); - } - - return await resp.json(); - } - // TODO return a strongly typed Task? // This method is used to store a task in RegisteredTask.task. // This is a raw Json type, so we can't use it to strongly type the task. @@ -133,13 +35,13 @@ export class OasstApiClient { } async ackTask(taskId: string, messageId: string): Promise { - return this.post(`/api/v1/tasks/${taskId}/ack`, { + await this.post(`/api/v1/tasks/${taskId}/ack`, { message_id: messageId, }); } async nackTask(taskId: string, reason: string): Promise { - return this.post(`/api/v1/tasks/${taskId}/nack`, { + await this.post(`/api/v1/tasks/${taskId}/nack`, { reason, }); } @@ -170,8 +72,8 @@ export class OasstApiClient { /** * Returns the tasks availability information for given `user`. */ - async fetch_tasks_availability(user: object): Promise { - return this.post("/api/v1/tasks/availability", user); + async fetch_tasks_availability(user: object): Promise { + return this.post("/api/v1/tasks/availability", user); } /** @@ -191,18 +93,12 @@ export class OasstApiClient { /** * Returns the `BackendUser` associated with `user_id` */ - async fetch_user(user_id: string): Promise { + async fetch_user(user_id: string): Promise { return this.get(`/api/v1/users/${user_id}`); } /** * Returns the set of `BackendUser`s stored by the backend. - * - * @param {number} max_count - The maximum number of users to fetch. - * @param {string} cursor - The user's `display_name` to use when paginating. - * @param {boolean} isForward - If true and `cursor` is not empty, pages - * forward. If false and `cursor` is not empty, pages backwards. - * @returns {Promise} A Promise that returns an array of `BackendUser` objects. */ async fetch_users({ direction, @@ -210,46 +106,28 @@ export class OasstApiClient { cursor, searchDisplayName, sortKey = "display_name", - }: FetchUsersParams): Promise { - const params = new URLSearchParams({ + }: FetchUsersParams): Promise { + return this.get(`/api/v1/users/cursor`, { search_text: searchDisplayName, sort_key: sortKey, - max_count: limit.toString(), + max_count: limit, + after: direction === "forward" ? cursor : undefined, + before: direction === "back" ? cursor : undefined, }); - - // The backend API uses different query parameters depending on the - // pagination direction but they both take the same cursor value. - // Depending on direction, pick the right query param. - if (cursor !== "") { - params.append(direction === "forward" ? "after" : "before", cursor); - } - const BASE_URL = `/api/v1/users/cursor`; - const url = `${BASE_URL}/?${params.toString()}`; - return this.get(url); } - // async fetch_user_by_display_name(name: string): Promise { - // const params = new URLSearchParams({ - // search_text: name, - // }); - - // const endpoint = `/api/v1/frontend_users/by_display_name`; - - // return this.get(`${endpoint}?${params.toString()}`); - // } - /** * Returns the `Message`s associated with `user_id` in the backend. */ - async fetch_user_messages(user_id: string): Promise { - return this.get(`/api/v1/users/${user_id}/messages`); + async fetch_user_messages(user_id: string): Promise { + return this.get(`/api/v1/users/${user_id}/messages`); } /** * Updates the backend's knowledge about the `user_id`. */ - async set_user_status(user_id: string, is_enabled: boolean, notes): Promise { - return this.put(`/api/v1/users/users/${user_id}?enabled=${is_enabled}¬es=${notes}`); + async set_user_status(user_id: string, is_enabled: boolean, notes: string): Promise { + await this.put(`/api/v1/users/users/${user_id}?enabled=${is_enabled}¬es=${notes}`); } /** @@ -265,18 +143,65 @@ export class OasstApiClient { async fetch_leaderboard( time_frame: LeaderboardTimeFrame, { limit = 20 }: { limit?: number } - ): Promise { - const params = new URLSearchParams({ - limit: limit.toString(), - }); - return this.get(`/api/v1/leaderboards/${time_frame}?${params.toString()}`); + ): Promise { + return this.get(`/api/v1/leaderboards/${time_frame}`, { limit }); } /** * Returns the counts of all tasks (some might be zero) */ - async fetch_available_tasks(user: BackendUserCore, lang: string): Promise { - return this.post(`/api/v1/tasks/availability?lang=${lang}`, user); + async fetch_available_tasks(user: BackendUserCore, lang: string): Promise { + return this.post(`/api/v1/tasks/availability?lang=${lang}`, user); + } + + private async post(path: string, body: unknown) { + return this.request("POST", path, { + body: JSON.stringify(body), + }); + } + + private async put(path: string) { + return this.request("PUT", path); + } + + private async get(path: string, query: Record = {}) { + const filteredQuery = Object.fromEntries( + Object.entries(query).filter(([, value]) => value !== undefined) + ) as Record; + + const params = new URLSearchParams(filteredQuery).toString(); + + return this.request("GET", `${path}${query ? `?${params}` : ""}`); + } + + private async request(method: "GET" | "POST" | "PUT", path: string, init?: RequestInit): Promise { + const resp = await fetch(`${this.oasstApiUrl}${path}`, { + method, + ...init, + headers: { + "X-API-Key": this.oasstApiKey, + "Content-Type": "application/json", + ...init?.headers, + }, + }); + + if (resp.status === 204) { + return null; + } + + if (resp.status >= 300) { + const errorText = await resp.text(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let error: any; + try { + error = JSON.parse(errorText); + } catch (e) { + throw new OasstError(errorText, 0, resp.status); + } + throw new OasstError(error.message ?? error, error.error_code, resp.status); + } + + return await resp.json(); } } diff --git a/website/src/types/Users.ts b/website/src/types/Users.ts index 39d2a663..52240b5f 100644 --- a/website/src/types/Users.ts +++ b/website/src/types/Users.ts @@ -51,3 +51,19 @@ export interface User extends BackendUser { */ role: string; } + +export type FetchUsersParams = { + limit: number; + cursor?: string; + direction: "forward" | "back"; + searchDisplayName?: string; + sortKey?: "username" | "display_name"; +}; + +export type FetchUsersResponse = { + items: T[]; + next?: string; + prev?: string; + sort_key: "username" | "display_name"; + order: "asc" | "desc"; +}; From 31630be319afc49a7db07ec4d176eefd5bfd4696 Mon Sep 17 00:00:00 2001 From: notmd Date: Wed, 25 Jan 2023 15:48:53 +0700 Subject: [PATCH 2/5] fix test & build --- website/{src => }/.prettierignore | 0 website/{src => }/.prettierrc.json | 0 .../contract/oasst_api_contract_tests.cy.ts | 17 +++++------------ website/src/lib/api.ts | 2 +- website/src/pages/api/admin/users.ts | 3 ++- 5 files changed, 8 insertions(+), 14 deletions(-) rename website/{src => }/.prettierignore (100%) rename website/{src => }/.prettierrc.json (100%) diff --git a/website/src/.prettierignore b/website/.prettierignore similarity index 100% rename from website/src/.prettierignore rename to website/.prettierignore diff --git a/website/src/.prettierrc.json b/website/.prettierrc.json similarity index 100% rename from website/src/.prettierrc.json rename to website/.prettierrc.json diff --git a/website/cypress/contract/oasst_api_contract_tests.cy.ts b/website/cypress/contract/oasst_api_contract_tests.cy.ts index d2ffeba3..0f217f85 100644 --- a/website/cypress/contract/oasst_api_contract_tests.cy.ts +++ b/website/cypress/contract/oasst_api_contract_tests.cy.ts @@ -12,25 +12,18 @@ describe("Contract test for Oasst API", function () { } as BackendUserCore; it("can fetch a task", async () => { - expect(await oasstApiClient.fetchTask("random", testUser)).to.be.not.null; + expect(await oasstApiClient.fetchTask("random", testUser, "en")).to.be.not.null; }); it("can ack a task", async () => { - const task = await oasstApiClient.fetchTask("random", testUser); - expect(await oasstApiClient.ackTask(task.id, "321")).to.be.null; + const task = await oasstApiClient.fetchTask("random", testUser, "en"); + expect(await oasstApiClient.ackTask(task.id, "321")).to.be.undefined; }); it("can record a taskInteraction", async () => { - const task = await oasstApiClient.fetchTask("random", testUser); + const task = await oasstApiClient.fetchTask("random", testUser, "en"); expect( - await oasstApiClient.interactTask( - "text_reply_to_message", - task.id, - "321", - "1", - { text: "Test" }, - testUser - ) + await oasstApiClient.interactTask("text_reply_to_message", task.id, "321", "1", { text: "Test" }, testUser, "en") ).to.be.not.null; }); diff --git a/website/src/lib/api.ts b/website/src/lib/api.ts index df4bd399..d61016d2 100644 --- a/website/src/lib/api.ts +++ b/website/src/lib/api.ts @@ -17,7 +17,7 @@ export const post = (url: string, { arg: data }) => api.post(url, data).then((re api.interceptors.response.use( (response) => response, (error) => { - throw new OasstError(error.message ?? error, error.error_code); + throw new OasstError(error.message ?? error, error.error_code, error?.response?.status || -1); } ); diff --git a/website/src/pages/api/admin/users.ts b/website/src/pages/api/admin/users.ts index 57944cff..d10c91b0 100644 --- a/website/src/pages/api/admin/users.ts +++ b/website/src/pages/api/admin/users.ts @@ -1,6 +1,7 @@ import { withRole } from "src/lib/auth"; -import { FetchUsersParams, oasstApiClient } from "src/lib/oasst_api_client"; +import { oasstApiClient } from "src/lib/oasst_api_client"; import prisma from "src/lib/prismadb"; +import { FetchUsersParams } from "src/types/Users"; /** * The number of users to fetch in a single request. Could later be a query parameter. From 97cd57a300cf8abfa149764eaa9f97e64e7dda2d Mon Sep 17 00:00:00 2001 From: notmd Date: Wed, 25 Jan 2023 16:02:20 +0700 Subject: [PATCH 3/5] fix test --- website/.prettierignore | 3 + website/README.md | 167 +++++++----------- website/cypress/README.md | 98 +++++----- website/cypress/components/Container.cy.tsx | 5 +- .../contract/oasst_api_contract_tests.cy.ts | 2 +- website/cypress/e2e/tasks/random.cy.ts | 4 +- website/cypress/support/commands.ts | 15 +- website/src/lib/oasst_api_client.ts | 8 +- website/styles/Home.module.css | 52 +++--- website/styles/Theme/index.tsx | 6 +- 10 files changed, 147 insertions(+), 213 deletions(-) diff --git a/website/.prettierignore b/website/.prettierignore index e69de29b..0be2a485 100644 --- a/website/.prettierignore +++ b/website/.prettierignore @@ -0,0 +1,3 @@ +.eslintrc.json +tailwind.config.js +.storybook/* diff --git a/website/README.md b/website/README.md index a30f2754..09b40e49 100644 --- a/website/README.md +++ b/website/README.md @@ -2,8 +2,7 @@ ## Purpose -This provides a comprehensive webapp interface for LAION's Open Assistant -project. Initially it will support: +This provides a comprehensive webapp interface for LAION's Open Assistant project. Initially it will support: 1. User registration using either Discord or Email. 1. Adding responses to incomplete Open Assistant tasks. @@ -11,8 +10,7 @@ project. Initially it will support: 1. Viewing an activity leaderboard. 1. Tracking community wide updates. -This interface compliments the Discord bot and will give access to the same -underlying tasks. +This interface compliments the Discord bot and will give access to the same underlying tasks. ## Contributing @@ -22,67 +20,54 @@ This website is built using: 1. [npm](https://www.npmjs.com/): The node package manager for building. 1. [React](https://reactjs.org/): The core frontend framework. -1. [Next.js](https://nextjs.org/): A React scaffolding framework to streamline - development. -1. [Prisma](https://www.prisma.io/): An ORM to interact with a web specific - [Postgres](https://www.postgresql.org/) database. -1. [NextAuth.js](https://next-auth.js.org/): A user authentication framework to - ensure we handle accounts with best practices. -1. [TailwindCSS](https://tailwindcss.com/): A general purpose framework for - styling any component. -1. [Chakra-UI](https://chakra-ui.com/): A wide collection of pre-built UI - components that generally look pretty good. +1. [Next.js](https://nextjs.org/): A React scaffolding framework to streamline development. +1. [Prisma](https://www.prisma.io/): An ORM to interact with a web specific [Postgres](https://www.postgresql.org/) + database. +1. [NextAuth.js](https://next-auth.js.org/): A user authentication framework to ensure we handle accounts with best + practices. +1. [TailwindCSS](https://tailwindcss.com/): A general purpose framework for styling any component. +1. [Chakra-UI](https://chakra-ui.com/): A wide collection of pre-built UI components that generally look pretty good. ### Set up your environment -To contribute to the website, make sure you have the following setup and -installed: +To contribute to the website, make sure you have the following setup and installed: -1. [NVM](https://github.com/nvm-sh/nvm): The Node Version Manager makes it easy - to ensure you have the right NodeJS version installed. Once installed, run - `nvm use 16` to use Node 16.x. The website is known to be stable with NodeJS +1. [NVM](https://github.com/nvm-sh/nvm): The Node Version Manager makes it easy to ensure you have the right NodeJS + version installed. Once installed, run `nvm use 16` to use Node 16.x. The website is known to be stable with NodeJS version 16.x. This will install both Node and NPM. -1. [Docker](https://www.docker.com/): We use docker to simplify running - dependent services. +1. [Docker](https://www.docker.com/): We use docker to simplify running dependent services. ### Getting everything up and running If you're doing active development we suggest the following workflow: 1. In one tab, navigate to the project root. -1. Run `docker compose up frontend-dev --build --attach-dependencies`. You can - optionally include `-d` to detach and later track the logs if desired. +1. Run `docker compose up frontend-dev --build --attach-dependencies`. You can optionally include `-d` to detach and + later track the logs if desired. 1. In another tab navigate to `${OPEN_ASSISTANT_ROOT/website`. 1. Run `npm ci` -1. Run `npx prisma db push` (This is also needed when you restart the docker - stack from scratch). -1. Run `npm run dev`. Now the website is up and running locally at - `http://localhost:3000`. -1. To create an account, login via the user using email authentication and - navigate to `http://localhost:1080`. Check the email listed and click the - log in link. You're now logged in and authenticated. +1. Run `npx prisma db push` (This is also needed when you restart the docker stack from scratch). +1. Run `npm run dev`. Now the website is up and running locally at `http://localhost:3000`. +1. To create an account, login via the user using email authentication and navigate to `http://localhost:1080`. Check + the email listed and click the log in link. You're now logged in and authenticated. ### Using debug user credentials -You can use the debug credentials provider to log in without fancy emails or -OAuth. +You can use the debug credentials provider to log in without fancy emails or OAuth. -1. This feature is automatically on in development mode, i.e. when you run - `npm run dev`. In case you want to do the same with a production build (for - example, the docker image), then run the website with environment variable +1. This feature is automatically on in development mode, i.e. when you run `npm run dev`. In case you want to do the + same with a production build (for example, the docker image), then run the website with environment variable `DEBUG_LOGIN=true`. 1. Use the `Login` button in the top right to go to the login page. -1. You should see a section for debug credentials. Enter any username you wish, - you will be logged in as that user. +1. You should see a section for debug credentials. Enter any username you wish, you will be logged in as that user. ### Using Storybook -To develop components using [Storybook](https://storybook.js.org/) run -`npm run storybook`. Then navigate to in your browser to -`http://localhost:6006`. +To develop components using [Storybook](https://storybook.js.org/) run `npm run storybook`. Then navigate to in your +browser to `http://localhost:6006`. -To create a new story create a file named `[componentName].stories.js`. An -example how such a story could look like, see `Header.stories.jsx`. +To create a new story create a file named `[componentName].stories.js`. An example how such a story could look like, see +`Header.stories.jsx`. ## Code Layout @@ -90,12 +75,10 @@ example how such a story could look like, see `Header.stories.jsx`. All react code is under `src/` with a few sub directories: -1. `pages/`: All pages a user could navigate too and API URLs which are under - `pages/api/`. -1. `components/`: All re-usable React components. If something gets used twice - we should create a component and put it here. -1. `lib/`: A generic place to store library files that are used anywhere. This - doesn't have much structure yet. +1. `pages/`: All pages a user could navigate too and API URLs which are under `pages/api/`. +1. `components/`: All re-usable React components. If something gets used twice we should create a component and put it + here. +1. `lib/`: A generic place to store library files that are used anywhere. This doesn't have much structure yet. NOTE: `styles/` can be ignored for now. @@ -113,25 +96,20 @@ We're not really using CSS styles. `styles/` can be ignored. ## Testing the UI -Cypress is used for end-to-end (e2e) and component testing and is configured in -`./cypress.config.ts`. The `./cypress` folder is used for supporting -configuration files etc. +Cypress is used for end-to-end (e2e) and component testing and is configured in `./cypress.config.ts`. The `./cypress` +folder is used for supporting configuration files etc. - Store e2e tests in the `./cypress/e2e` folder. -- Store component tests adjacent to the component being tested. If you want to - wriite a test for `./src/components/Layout.tsx` then store the test file at - `./src/components/Layout.cy.tsx`. +- Store component tests adjacent to the component being tested. If you want to wriite a test for + `./src/components/Layout.tsx` then store the test file at `./src/components/Layout.cy.tsx`. A few npm scripts are available for convenience: -- `npm run cypress`: Useful for development, it opens Cypress and allows you to - explore, run and debug tests. It assumes you have the NextJS site running at - `localhost:3000`. -- `npm run cypress:run`: Runs all tests. Useful for a quick sanity check before - sending a PR or to run in CI pipelines. -- `npm run cypress:image-baseline`: If you have tests failing because of visual - changes that was expected, this command will update the baseline images stored - in `./cypress-visual-screenshots/baseline` with those from the adjacent +- `npm run cypress`: Useful for development, it opens Cypress and allows you to explore, run and debug tests. It assumes + you have the NextJS site running at `localhost:3000`. +- `npm run cypress:run`: Runs all tests. Useful for a quick sanity check before sending a PR or to run in CI pipelines. +- `npm run cypress:image-baseline`: If you have tests failing because of visual changes that was expected, this command + will update the baseline images stored in `./cypress-visual-screenshots/baseline` with those from the adjacent comparison folder. More can be found in the [docs of `uktrade/cypress-image-diff`](https://github.com/uktrade/cypress-image-diff/blob/main/docs/CLI.md#update-all-baseline-images-for-failing-tests). @@ -141,10 +119,9 @@ Read more in the [./cypress README](cypress/). Jest and React Testing Library are used for unit testing JS/TS/TSX code. -- Store unit test files adjacent to the file being tested and have the filename - end with `.test.ts` for non-React code or `.test.tsx` for React code. -- `npm run jest`: automatically runs tests and watches for any relevant changes - to rerun tests. +- Store unit test files adjacent to the file being tested and have the filename end with `.test.ts` for non-React code + or `.test.tsx` for React code. +- `npm run jest`: automatically runs tests and watches for any relevant changes to rerun tests. Read more in the [./src/README.md](src/README.md). @@ -152,30 +129,25 @@ Read more in the [./src/README.md](src/README.md). When writing code for the website, we have a few best practices: -1. When importing packages import external dependencies first then local - dependencies. Order them alphabetically according to the package name. -1. When trying to implement something new, check if - [Chakra-UI](https://chakra-ui.com/) has components that are close enough to - your need. For example Sliders, Radio Buttons, Progress indicators, etc. - They have a lot and we can save time by re-using what they have and tweaking - the style as needed. -1. Format everything with [Prettier](https://prettier.io/). This is done by - default with pre-submits. We currently don't have any custom settings. -1. Define functional React components (with types for all properties when - feasible). +1. When importing packages import external dependencies first then local dependencies. Order them alphabetically + according to the package name. +1. When trying to implement something new, check if [Chakra-UI](https://chakra-ui.com/) has components that are close + enough to your need. For example Sliders, Radio Buttons, Progress indicators, etc. They have a lot and we can save + time by re-using what they have and tweaking the style as needed. +1. Format everything with [Prettier](https://prettier.io/). This is done by default with pre-submits. We currently + don't have any custom settings. +1. Define functional React components (with types for all properties when feasible). ### Developing New Features -When working on new features or making significant changes that can't be done -within a single Pull Request, we ask that you make use of Feature Flags. +When working on new features or making significant changes that can't be done within a single Pull Request, we ask that +you make use of Feature Flags. -We've set up -[`react-feature-flags`](https://www.npmjs.com/package/react-feature-flags) to -make this easier. To get started: +We've set up [`react-feature-flags`](https://www.npmjs.com/package/react-feature-flags) to make this easier. To get +started: -1. Add a new flag entry to `website/src/flags.ts`. We have an example flag you - can copy as an example. Be sure to `isActive` to true when testing your - features but false when submitting your PR. +1. Add a new flag entry to `website/src/flags.ts`. We have an example flag you can copy as an example. Be sure to + `isActive` to true when testing your features but false when submitting your PR. 1. Use your flag wherever you add a new UI element. This can be done with: ```js @@ -188,29 +160,24 @@ import { Flags } from "react-feature-flags"; You can see an example of how this works by checking `website/src/components/Header/Headers.tsx` where we use `flagTest`. -1. Once you've finished building out the feature and it is ready for everyone - to use, it's safe to remove the `Flag` wrappers around your component and - the entry in `flags.ts`. +1. Once you've finished building out the feature and it is ready for everyone to use, it's safe to remove the `Flag` + wrappers around your component and the entry in `flags.ts`. ### URL Paths -To use stable and consistent URL paths, we recommend the following strategy for -new tasks: +To use stable and consistent URL paths, we recommend the following strategy for new tasks: -1. For any task that involves writing a free-form response, put the page under - `website/src/pages/create` with a page name matching the task type, such as - `initial_prompt.tsx`. -1. For any task that evaluates, rates, or ranks content, put the page under - `website/src/pages/evaluate` with a page name matching the task type such as - `rank_initial_prompts.tsx`. +1. For any task that involves writing a free-form response, put the page under `website/src/pages/create` with a page + name matching the task type, such as `initial_prompt.tsx`. +1. For any task that evaluates, rates, or ranks content, put the page under `website/src/pages/evaluate` with a page + name matching the task type such as `rank_initial_prompts.tsx`. -With this we'll be able to ensure these contribution pages are hidden from -logged out users but accessible to logged in users. +With this we'll be able to ensure these contribution pages are hidden from logged out users but accessible to logged in +users. ## Learn More To learn more about Next.js, take a look at the following resources: -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js - features and API. +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. diff --git a/website/cypress/README.md b/website/cypress/README.md index 4750cbf6..d6a2b383 100644 --- a/website/cypress/README.md +++ b/website/cypress/README.md @@ -1,24 +1,19 @@ # Component and e2e testing with Cypress -[Cypress](https://www.cypress.io/) is used for both component- and end-to-end -testing. Below there's a few examples for the context of this site. To learn -more, the -[Cypress documentation](https://docs.cypress.io/guides/getting-started/opening-the-app) -has it all. +[Cypress](https://www.cypress.io/) is used for both component- and end-to-end testing. Below there's a few examples for +the context of this site. To learn more, the +[Cypress documentation](https://docs.cypress.io/guides/getting-started/opening-the-app) has it all. -Don't get scared by the commercial offerings they offer. Their core is open -source, the cloud offering is not necesarry at all and can be replaced by CI -tooling and [community efforts](https://sorry-cypress.dev/). +Don't get scared by the commercial offerings they offer. Their core is open source, the cloud offering is not necesarry +at all and can be replaced by CI tooling and [community efforts](https://sorry-cypress.dev/). # Component testing -To write a new component test, you either create a new `.tsx` adjacent to the -component you want to test or you can use the guide presented yo you when -running `npm run cypress` which allows you to easily create the skeleton test -for an existing component. +To write a new component test, you either create a new `.tsx` adjacent to the component you want to test or you can use +the guide presented yo you when running `npm run cypress` which allows you to easily create the skeleton test for an +existing component. -If you have a `Button.tsx` component, create a file next to it called -`Button.cy.tsx` which could look like this: +If you have a `Button.tsx` component, create a file next to it called `Button.cy.tsx` which could look like this: ```typescript import React from "react"; @@ -35,28 +30,24 @@ describe("