diff --git a/.maestro/config.yml b/.maestro/config.yml new file mode 100644 index 0000000000..b36b0ef601 --- /dev/null +++ b/.maestro/config.yml @@ -0,0 +1,2 @@ +flows: + - "flows/*" \ No newline at end of file diff --git a/.maestro/flows/composer.yml b/.maestro/flows/composer.yml new file mode 100644 index 0000000000..f6d760ea5f --- /dev/null +++ b/.maestro/flows/composer.yml @@ -0,0 +1,87 @@ +appId: xyz.blueskyweb.app +--- +- runScript: + file: ../setupServer.js + env: + SERVER_PATH: ?users +- runFlow: + file: ../setupApp.yml +- tapOn: + id: "e2eSignInAlice" +- tapOn: + id: "composeFAB" +- inputText: "Post text only" +- tapOn: + id: "composerPublishBtn" +- assertVisible: + id: "composeFAB" +- tapOn: + id: "composeFAB" +- inputText: "Post with an image" +- tapOn: + id: "openGalleryBtn" +- tapOn: + id: "composerPublishBtn" +- assertVisible: + id: "composeFAB" +- tapOn: + id: "composeFAB" +- inputText: "Post with a https://example.com link card" +- tapOn: + id: "composerPublishBtn" +- assertVisible: + id: "composeFAB" +- tapOn: + id: "e2eRefreshHome" +- tapOn: + id: "replyBtn" +- inputText: "Reply text only" +- tapOn: + id: "composerPublishBtn" +- assertVisible: + id: "composeFAB" +- tapOn: + id: "replyBtn" +- inputText: "Reply with an image" +- tapOn: + id: "openGalleryBtn" +- tapOn: + id: "composerPublishBtn" +- assertVisible: + id: "composeFAB" +- tapOn: + id: "replyBtn" +- inputText: "Reply with a https://example.com link card" +- tapOn: + id: "composerPublishBtn" +- assertVisible: + id: "composeFAB" +- tapOn: + id: "repostBtn" +- tapOn: + id: "quoteBtn" +- inputText: "QP text only" +- tapOn: + id: "composerPublishBtn" +- assertVisible: + id: "composeFAB" +- tapOn: + id: "repostBtn" +- tapOn: + id: "quoteBtn" +- inputText: "QP with an image" +- tapOn: + id: "openGalleryBtn" +- tapOn: + id: "composerPublishBtn" +- assertVisible: + id: "composeFAB" +- tapOn: + id: "repostBtn" +- tapOn: + id: "quoteBtn" +- inputText: "QP with a https://example.com link card" +- tapOn: + id: "composerPublishBtn" +- assertVisible: + id: "composeFAB" diff --git a/.maestro/flows/create-account.yml b/.maestro/flows/create-account.yml new file mode 100644 index 0000000000..99ac1371a5 --- /dev/null +++ b/.maestro/flows/create-account.yml @@ -0,0 +1,37 @@ +appId: xyz.blueskyweb.app +--- +- runScript: + file: ../setupServer.js + env: + SERVER_PATH: "" +- runFlow: + file: ../setupApp.yml +- tapOn: + id: "e2eOpenLoggedOutView" +- tapOn: + id: "createAccountButton" +- tapOn: + id: "selectServiceButton" +- tapOn: + id: "customSelectBtn" +- tapOn: + id: "customServerTextInput" +- inputText: "http://localhost:3000" +- pressKey: Enter +- tapOn: + id: "doneBtn" +- tapOn: + id: "emailInput" +- inputText: "example@test.com" +- tapOn: + id: "passwordInput" +- inputText: "hunter2" +- pressKey: Enter +- tapOn: + id: "nextBtn" +- tapOn: + id: "handleInput" +- inputText: "e2e-test" +- tapOn: + id: "nextBtn" + diff --git a/.maestro/flows/curate-lists.yml b/.maestro/flows/curate-lists.yml new file mode 100644 index 0000000000..35f4f800dc --- /dev/null +++ b/.maestro/flows/curate-lists.yml @@ -0,0 +1,208 @@ +appId: xyz.blueskyweb.app +--- +- runScript: + file: ../setupServer.js + env: + SERVER_PATH: "?users&follows&posts" +- runFlow: + file: ../setupApp.yml +- tapOn: + id: "e2eSignInAlice" + +- tapOn: + label: "Create a curate list" + id: "e2eGotoLists" +- tapOn: + id: "newUserListBtn" +- assertVisible: + id: "createOrEditListModal" +- tapOn: + id: "editNameInput" +- inputText: "Good Ppl" +- tapOn: + id: "editDescriptionInput" +- inputText: "They good" +- tapOn: "Save" +- tapOn: "Save" +- assertNotVisible: + id: "createOrEditListModal" +- tapOn: "About" +- assertVisible: "Good Ppl" +- assertVisible: "They good" + +- tapOn: + label: "Edit display name and description via the edit curatelist modal" + point: "90%,9%" +- tapOn: "Edit list details" +- assertVisible: + id: "createOrEditListModal" +- tapOn: + id: "editNameInput" +- eraseText +- inputText: "Bad Ppl" +- hideKeyboard +- tapOn: + id: "editDescriptionInput" +- eraseText +- inputText: "They bad" +- tapOn: "Save" +- tapOn: "Save" +- assertNotVisible: + id: "createOrEditListModal" +- assertVisible: Bad Ppl +- assertVisible: They bad + +- tapOn: + label: "Remove description via the edit curatelist modal" + point: "90%,9%" +- tapOn: "Edit list details" +- assertVisible: + id: "createOrEditListModal" +- tapOn: + id: "editDescriptionInput" +- eraseText +- tapOn: "Save" +- tapOn: "Save" +- assertNotVisible: + id: "createOrEditListModal" +- assertNotVisible: + id: "listDescription" + +- tapOn: + label: "Delete the curatelist" + point: "90%,9%" +- tapOn: "Delete List" +- tapOn: + id: "confirmBtn" +- assertVisible: + id: "listsEmpty" + +- tapOn: + label: "Create a new curatelist" + id: "e2eGotoLists" +- tapOn: + id: "newUserListBtn" +- assertVisible: + id: "createOrEditListModal" +- tapOn: + id: "editNameInput" +- inputText: "Good Ppl" +- tapOn: + id: "editDescriptionInput" +- inputText: "They good" +- tapOn: "Save" +- tapOn: "Save" +- assertNotVisible: + id: "createOrEditListModal" +- tapOn: "About" +- assertVisible: "Good Ppl" +- assertVisible: "They good" +- tapOn: "About" + +- tapOn: + label: "Adds users on curatelists from the list" + id: "addUserBtn" +- assertVisible: + id: "listAddUserModal" +- tapOn: + id: "searchInput" +- inputText: "b" +- pressKey: Enter +- tapOn: + id: "user-bob.test-addBtn" +- tapOn: + id: "doneBtn" +- assertNotVisible: + id: "listAddUserModal" +- assertVisible: + id: "user-bob.test" + +- tapOn: "Posts" +- assertVisible: + label: "Shows posts by the users in the list" + id: "feedItem-by-bob.test" + +- tapOn: + label: "Pins the list" + id: "pinBtn" +- tapOn: + id: "e2eGotoHome" +- tapOn: "Good Ppl" +- assertVisible: + id: "feedItem-by-bob.test" +- tapOn: + id: "bottomBarFeedsBtn" +- tapOn: + id: "saved-feed-Good Ppl" +- assertVisible: + id: "feedItem-by-bob.test" +- tapOn: + id: "unpinBtn" +- tapOn: + id: "bottomBarHomeBtn" +- assertNotVisible: + id: "homeScreenFeedTabs-Good Ppl" +- tapOn: + id: "e2eGotoLists" +- tapOn: + id: "list-Good Ppl" + +- tapOn: "About" +- assertVisible: + label: "Removes users on curatelists from the list" + id: "user-bob.test" +- tapOn: + point: "90%,43%" +- assertVisible: + id: "userAddRemoveListsModal" +- tapOn: + id: "user-bob.test-addBtn" +- tapOn: + id: "doneBtn" +- assertNotVisible: + id: "userAddRemoveListsModal" + +- tapOn: + label: "Shows the curatelist on my profile" + id: "bottomBarProfileBtn" +- swipe: + from: + id: "profilePager-selector" + direction: LEFT +- tapOn: + id: "profilePager-selector-5" +- tapOn: + id: "list-Good Ppl" + +- tapOn: + label: "Adds and removes users on curatelists from the profile" + id: "bottomBarSearchBtn" +- tapOn: + id: "searchTextInput" +- inputText: "bob" +- tapOn: + id: "searchAutoCompleteResult-bob.test" +- assertVisible: + id: "profileView" +- tapOn: + id: "profileHeaderDropdownBtn" +- tapOn: "Add to Lists" +- assertVisible: + id: "userAddRemoveListsModal" +- tapOn: + id: "user-bob.test-addBtn" +- tapOn: + id: "doneBtn" +- assertNotVisible: + id: "userAddRemoveListsModal" +- tapOn: + id: "profileHeaderDropdownBtn" +- tapOn: "Add to Lists" +- assertVisible: + id: "userAddRemoveListsModal" +- tapOn: + id: "user-bob.test-addBtn" +- tapOn: + id: "doneBtn" +- assertNotVisible: + id: "userAddRemoveListsModal" diff --git a/.maestro/flows/home-screen.yml b/.maestro/flows/home-screen.yml new file mode 100644 index 0000000000..69a1fe37f4 --- /dev/null +++ b/.maestro/flows/home-screen.yml @@ -0,0 +1,63 @@ +appId: xyz.blueskyweb.app +--- +- runScript: + file: ../setupServer.js + env: + SERVER_PATH: ?users&follows&posts&feeds +- runFlow: + file: ../setupApp.yml +- tapOn: + id: "e2eSignInAlice" + +- tapOn: + label: "Can go to feeds page using feeds button in tab bar" + text: "Feeds ✨" +- assertVisible: "Discover New Feeds" + +- tapOn: + label: "Feeds button disappears after pinning a feed" + id: "bottomBarProfileBtn" +- swipe: + from: + id: "profilePager-selector" + direction: LEFT +- tapOn: + id: "profilePager-selector-4" +- tapOn: + id: "feed-alice-favs" +- tapOn: "Pin to Home" +- tapOn: + id: "bottomBarHomeBtn" +- assertNotVisible: "Feeds ✨" + +- tapOn: + label: "Can like posts" + id: "likeBtn" +- assertVisible: + id: "likeCount" + text: "1" +- tapOn: + id: "likeBtn" +- assertNotVisible: + id: "likeCount" + +- tapOn: + label: "Can repost posts" + id: "repostBtn" +- tapOn: "Repost" +- assertVisible: + id: "repostCount" + text: "1" +- tapOn: + id: "repostBtn" +- tapOn: "Undo repost" +- assertNotVisible: + id: "repostCount" + +- tapOn: + label: "Can delete posts" + id: "postDropdownBtn" + childOf: + id: "feedItem-by-alice.test" +- tapOn: "Delete post" +- tapOn: "Delete" diff --git a/.maestro/setupApp.yml b/.maestro/setupApp.yml new file mode 100644 index 0000000000..8c3ffd2d3b --- /dev/null +++ b/.maestro/setupApp.yml @@ -0,0 +1,11 @@ +appId: xyz.blueskyweb.app +--- +- launchApp: + appId: "xyz.blueskyweb.app" + clearState: true +- waitForAnimationToEnd +- tapOn: "http://localhost:8081" +- waitForAnimationToEnd +- swipe: + from: "Bluesky" + direction: DOWN diff --git a/.maestro/setupServer.js b/.maestro/setupServer.js new file mode 100644 index 0000000000..78c82603c8 --- /dev/null +++ b/.maestro/setupServer.js @@ -0,0 +1,4 @@ +http.post('http://localhost:1986/' + SERVER_PATH, { + headers: {'Content-Type': 'text/plain'}, + body: '', +}) diff --git a/docs/build.md b/docs/build.md index deab91a5ba..88733d3b09 100644 --- a/docs/build.md +++ b/docs/build.md @@ -16,10 +16,6 @@ - Add `eval "$(rbenv init - zsh)"` to your `~/.zshrc` - From inside the project directory: - `bundler install` (this will install Cocoapods) -- Setup your environment [for e2e testing using detox](https://wix.github.io/Detox/docs/introduction/getting-started): - - `yarn global add detox-cli` - - `brew tap wix/brew` - - `brew install applesimutils` - After initial setup: - Copy `google-services.json.example` to `google-services.json` or provide your own `google-services.json`. (A real firebase project is NOT required) - `npx expo prebuild` -> you will also need to run this anytime `app.json` or native `package.json` deps change @@ -120,10 +116,7 @@ To open the [Developer Menu](https://docs.expo.dev/debugging/tools/#developer-me ### Running E2E Tests -- Make sure you've set your environment following the above -- Make sure Metro and the dev server are running -- Run `yarn e2e` -- Find the artifacts in the `artifact` folder +See [testing.md](./testing.md). ### Polyfills diff --git a/docs/testing.md b/docs/testing.md index e9b9445e04..ae0a424fbd 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -3,13 +3,19 @@ Make sure you've copied `.env.example` to `.env.test` and provided any required values. -### Using Maestro E2E tests +## Using Maestro + 1. Install Maestro by following [these instructions](https://maestro.mobile.dev/getting-started/installing-maestro). This will help us run the E2E tests. -2. You can write Maestro tests in `__e2e__/maestro` directory by creating a new `.yaml` file or by modifying an existing one. -3. You can also use [Maestro Studio](https://maestro.mobile.dev/getting-started/maestro-studio) which automatically generates commands by recording your actions on the app. Therefore, you can create realistic tests without having to manually write any code. Use the `maestro studio` command to start recording your actions. +2. You can write Maestro tests in `/.maestro/flows/` directory by creating a new `.yml` file or by modifying an existing one. +3. You can also use [Maestro Studio](https://maestro.mobile.dev/getting-started/maestro-studio) which automatically generates commands by recording your actions on the app. Therefore, you can create realistic tests without having to manually write any code. Use the `maestro studio` command to start recording your actions. +### Running Maestro tests -### Using Flashlight for Performance Testing +- In one tab, run `yarn e2e:mock-server` +- In a second tab, run `yarn e2e:metro` +- In a third tab, run `yarn e2e:run` + +## Using Flashlight for Performance Testing 1. Make sure Maestro is installed (optional: only for automated testing) by following the instructions above 2. Install Flashlight by following [these instructions](https://docs.flashlight.dev/) 3. The simplest way to get started is by running `yarn perf:measure` which will run a live preview of the performance test results. You can [see a demo here](https://github.com/bamlab/flashlight/assets/4534323/4038a342-f145-4c3b-8cde-17949bf52612) diff --git a/jest/test-pds.ts b/jest/test-pds.ts index 1c52d944c6..2fe623ca98 100644 --- a/jest/test-pds.ts +++ b/jest/test-pds.ts @@ -114,8 +114,7 @@ export async function createServer( pdsUrl, mocker: new Mocker(testNet, pdsUrl, pic), async close() { - await testNet.pds.server.destroy() - await testNet.plc.server.destroy() + await testNet.close() }, } } diff --git a/package.json b/package.json index 4ed2b933fe..a442a1d0d6 100644 --- a/package.json +++ b/package.json @@ -31,9 +31,9 @@ "lint": "eslint --cache --ext .js,.jsx,.ts,.tsx src", "typecheck": "tsc --project ./tsconfig.check.json", "e2e:mock-server": "./jest/dev-infra/with-test-redis-and-db.sh ts-node --project tsconfig.e2e.json __e2e__/mock-server.ts", - "e2e:metro": "NODE_ENV=test RN_SRC_EXT=e2e.ts,e2e.tsx expo run:ios", + "e2e:metro": "EXPO_PUBLIC_ENV=e2e NODE_ENV=test RN_SRC_EXT=e2e.ts,e2e.tsx expo run:ios", "e2e:build": "NODE_ENV=test detox build -c ios.sim.debug", - "e2e:run": "NODE_ENV=test detox test --configuration ios.sim.debug --take-screenshots all", + "e2e:run": "maestro test .maestro", "perf:test": "NODE_ENV=test maestro test", "perf:test:run": "NODE_ENV=test maestro test __e2e__/maestro/scroll.yaml", "perf:test:measure": "NODE_ENV=test flashlight test --bundleId xyz.blueskyweb.app --testCommand \"yarn perf:test\" --duration 150000 --resultsFilePath .perf/results.json", diff --git a/src/view/com/testing/TestCtrls.e2e.tsx b/src/view/com/testing/TestCtrls.e2e.tsx index 1eb99c4f5e..1c82a712ed 100644 --- a/src/view/com/testing/TestCtrls.e2e.tsx +++ b/src/view/com/testing/TestCtrls.e2e.tsx @@ -1,11 +1,14 @@ import React from 'react' -import {Pressable, View} from 'react-native' -import {navigate} from '../../../Navigation' -import {useModalControls} from '#/state/modals' +import {LogBox, Pressable, View} from 'react-native' import {useQueryClient} from '@tanstack/react-query' -import {useSessionApi} from '#/state/session' + +import {useModalControls} from '#/state/modals' import {useSetFeedViewPreferencesMutation} from '#/state/queries/preferences' +import {useSessionApi} from '#/state/session' import {useLoggedOutViewControls} from '#/state/shell/logged-out' +import {navigate} from '../../../Navigation' + +LogBox.ignoreAllLogs() /** * This utility component is only included in the test simulator diff --git a/src/view/com/util/Toast.e2e.tsx b/src/view/com/util/Toast.e2e.tsx new file mode 100644 index 0000000000..c5582ff0a8 --- /dev/null +++ b/src/view/com/util/Toast.e2e.tsx @@ -0,0 +1 @@ +export function show() {}