From 5f553c29dfdac1bf81debe5e28983a9dbe74bd7f Mon Sep 17 00:00:00 2001 From: Eric Bailey Date: Tue, 5 Dec 2023 14:50:56 -0600 Subject: [PATCH] =?UTF-8?q?E2E=20=F0=9F=9F=A2=20=20(#2092)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add logged out e2e ctrl, fix login test * Fix log handling via env vars in expo * Fix create account test * Upgrade dev-env * Fix home screen tests * Fix composer tests * Fix curate-lists tests, split in two * Fix invite codes test * Fix curate-lists tests * Give up on mergefeed test * Fix mod lists * Fix app view url * Fix profile tests * Fix profile test with hack * Keep using globals * Fix two more * Fix thread view * Better skip for merge feed * Revert debug code --- .env.example | 3 + __e2e__/tests/composer.test.ts | 4 + __e2e__/tests/create-account.test.ts | 10 + __e2e__/tests/curate-lists.test.ts | 33 +- __e2e__/tests/home-screen.test.ts | 2 + __e2e__/tests/invite-codes.test.ts | 19 +- __e2e__/tests/login.test.ts | 4 + ...e-feed.test.ts => merge-feed.test.skip.ts} | 6 + __e2e__/tests/mod-lists.test.ts | 31 +- __e2e__/tests/profile-screen.test.ts | 19 +- __e2e__/tests/search-screen.test.ts | 2 + __e2e__/tests/self-labeling.test.ts | 2 + __e2e__/tests/thread-muting.test.ts | 4 +- __e2e__/tests/thread-screen.test.ts | 14 +- docs/testing.md | 5 +- index.js | 12 +- jest/test-pds.ts | 8 +- package.json | 8 +- src/env.ts | 2 +- src/state/modals/index.tsx | 1 + src/view/com/feeds/FeedSourceCard.tsx | 1 + src/view/com/lists/ListMembers.tsx | 1 + src/view/com/modals/UserAddRemoveLists.tsx | 7 +- src/view/com/post-thread/PostThreadItem.tsx | 10 +- src/view/com/profile/ProfileHeader.tsx | 1 + src/view/com/testing/TestCtrls.e2e.tsx | 8 + src/view/com/util/Toast.tsx | 2 + src/view/shell/desktop/Search.tsx | 1 + yarn.lock | 1614 +++++++++++++++-- 29 files changed, 1600 insertions(+), 234 deletions(-) rename __e2e__/tests/{merge-feed.test.ts => merge-feed.test.skip.ts} (93%) diff --git a/.env.example b/.env.example index d4db46ab..5cf8e07b 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,6 @@ +# Copy this to `.env` and `.env.test` files + SENTRY_AUTH_TOKEN= +EXPO_PUBLIC_ENV=development EXPO_PUBLIC_LOG_LEVEL=debug EXPO_PUBLIC_LOG_DEBUG= diff --git a/__e2e__/tests/composer.test.ts b/__e2e__/tests/composer.test.ts index 6251ad0c..34f8ae39 100644 --- a/__e2e__/tests/composer.test.ts +++ b/__e2e__/tests/composer.test.ts @@ -1,5 +1,7 @@ /* eslint-env detox/detox */ +import {describe, beforeAll, it} from '@jest/globals' +import {expect} from 'detox' import {openApp, loginAsAlice, createServer, sleep} from '../util' describe('Composer', () => { @@ -45,6 +47,8 @@ describe('Composer', () => { }) it('Reply text only', async () => { + await element(by.id('e2eRefreshHome')).tap() + const post = by.id('feedItem-by-alice.test') await element(by.id('replyBtn').withAncestor(post)).atIndex(0).tap() await element(by.id('composerTextInput')).typeText('Reply text only') diff --git a/__e2e__/tests/create-account.test.ts b/__e2e__/tests/create-account.test.ts index 283eda34..eab3e538 100644 --- a/__e2e__/tests/create-account.test.ts +++ b/__e2e__/tests/create-account.test.ts @@ -1,5 +1,7 @@ /* eslint-env detox/detox */ +import {describe, beforeAll, it} from '@jest/globals' +import {expect} from 'detox' import {openApp, createServer} from '../util' describe('Create account', () => { @@ -10,6 +12,8 @@ describe('Create account', () => { }) it('I can create a new account', async () => { + await element(by.id('e2eOpenLoggedOutView')).tap() + await element(by.id('createAccountButton')).tap() await device.takeScreenshot('1- opened create account screen') await element(by.id('otherServerBtn')).tap() @@ -17,14 +21,20 @@ describe('Create account', () => { await element(by.id('customServerInput')).clearText() await element(by.id('customServerInput')).typeText(service) await device.takeScreenshot('3- input test server URL') + await element(by.id('nextBtn')).tap() + await element(by.id('emailInput')).typeText('example@test.com') await element(by.id('passwordInput')).typeText('hunter2') await device.takeScreenshot('4- entered account details') + await element(by.id('nextBtn')).tap() + await element(by.id('handleInput')).typeText('e2e-test') await device.takeScreenshot('4- entered handle') + await element(by.id('nextBtn')).tap() + await expect(element(by.id('welcomeOnboarding'))).toBeVisible() await element(by.id('continueBtn')).tap() await expect(element(by.id('recommendedFeedsOnboarding'))).toBeVisible() diff --git a/__e2e__/tests/curate-lists.test.ts b/__e2e__/tests/curate-lists.test.ts index b7c6c25c..f188b154 100644 --- a/__e2e__/tests/curate-lists.test.ts +++ b/__e2e__/tests/curate-lists.test.ts @@ -1,5 +1,7 @@ /* eslint-env detox/detox */ +import {describe, beforeAll, it} from '@jest/globals' +import {expect} from 'detox' import {openApp, loginAsAlice, loginAsBob, createServer, sleep} from '../util' describe('Curate lists', () => { @@ -11,7 +13,6 @@ describe('Curate lists', () => { }) it('Login and create a curatelists', async () => { - await expect(element(by.id('signInButton'))).toBeVisible() await loginAsAlice() await element(by.id('e2eGotoLists')).tap() await element(by.id('newUserListBtn')).tap() @@ -27,7 +28,7 @@ describe('Curate lists', () => { it('Edit display name and description via the edit curatelist modal', async () => { await element(by.id('headerDropdownBtn')).tap() - await element(by.text('Edit List Details')).tap() + await element(by.text('Edit list details')).tap() await expect(element(by.id('createOrEditListModal'))).toBeVisible() await element(by.id('editNameInput')).clearText() await element(by.id('editNameInput')).typeText('Bad Ppl') @@ -45,7 +46,7 @@ describe('Curate lists', () => { it('Remove description via the edit curatelist modal', async () => { await element(by.id('headerDropdownBtn')).tap() - await element(by.text('Edit List Details')).tap() + await element(by.text('Edit list details')).tap() await expect(element(by.id('createOrEditListModal'))).toBeVisible() await element(by.id('editDescriptionInput')).clearText() await element(by.id('saveBtn')).tap() @@ -60,7 +61,7 @@ describe('Curate lists', () => { it('Set avi via the edit curatelist modal', async () => { await expect(element(by.id('userAvatarFallback'))).toExist() await element(by.id('headerDropdownBtn')).tap() - await element(by.text('Edit List Details')).tap() + await element(by.text('Edit list details')).tap() await expect(element(by.id('createOrEditListModal'))).toBeVisible() await element(by.id('changeAvatarBtn')).tap() await element(by.text('Library')).tap() @@ -77,7 +78,7 @@ describe('Curate lists', () => { it('Remove avi via the edit curatelist modal', async () => { await expect(element(by.id('userAvatarImage'))).toExist() await element(by.id('headerDropdownBtn')).tap() - await element(by.text('Edit List Details')).tap() + await element(by.text('Edit list details')).tap() await expect(element(by.id('createOrEditListModal'))).toBeVisible() await element(by.id('changeAvatarBtn')).tap() await element(by.text('Remove')).tap() @@ -98,6 +99,7 @@ describe('Curate lists', () => { }) it('Create a new curatelist', async () => { + await element(by.id('e2eGotoLists')).tap() await element(by.id('newUserListBtn')).tap() await expect(element(by.id('createOrEditListModal'))).toBeVisible() await element(by.id('editNameInput')).typeText('Good Ppl') @@ -128,6 +130,7 @@ describe('Curate lists', () => { }) it('Pins the list', async () => { + await expect(element(by.id('pinBtn'))).toBeVisible() await element(by.id('pinBtn')).tap() await element(by.id('e2eGotoHome')).tap() await element(by.id('homeScreenFeedTabs-Good Ppl')).tap() @@ -152,15 +155,15 @@ describe('Curate lists', () => { await expect(element(by.id('user-bob.test'))).toBeVisible() await element(by.id('user-bob.test-editBtn')).tap() await expect(element(by.id('userAddRemoveListsModal'))).toBeVisible() - await element(by.id('toggleBtn-Good Ppl')).tap() - await element(by.id('saveBtn')).tap() + await element(by.id('user-bob.test-addBtn')).tap() + await element(by.id('doneBtn')).tap() await expect(element(by.id('userAddRemoveListsModal'))).not.toBeVisible() }) it('Shows the curatelist on my profile', async () => { await element(by.id('bottomBarProfileBtn')).tap() - await element(by.id('selector')).swipe('left') - await element(by.id('selector-4')).tap() + await element(by.id('profilePager-selector')).swipe('left') + await element(by.id('profilePager-selector-5')).tap() await element(by.id('list-Good Ppl')).tap() }) @@ -173,15 +176,15 @@ describe('Curate lists', () => { await element(by.id('profileHeaderDropdownBtn')).tap() await element(by.text('Add to Lists')).tap() await expect(element(by.id('userAddRemoveListsModal'))).toBeVisible() - await element(by.id('toggleBtn-Good Ppl')).tap() - await element(by.id('saveBtn')).tap() + await element(by.id('user-bob.test-addBtn')).tap() + await element(by.id('doneBtn')).tap() await expect(element(by.id('userAddRemoveListsModal'))).not.toBeVisible() await element(by.id('profileHeaderDropdownBtn')).tap() await element(by.text('Add to Lists')).tap() await expect(element(by.id('userAddRemoveListsModal'))).toBeVisible() - await element(by.id('toggleBtn-Good Ppl')).tap() - await element(by.id('saveBtn')).tap() + await element(by.id('user-bob.test-addBtn')).tap() + await element(by.id('doneBtn')).tap() await expect(element(by.id('userAddRemoveListsModal'))).not.toBeVisible() }) @@ -192,8 +195,8 @@ describe('Curate lists', () => { await element(by.id('bottomBarSearchBtn')).tap() await element(by.id('searchTextInput')).typeText('alice') await element(by.id('searchAutoCompleteResult-alice.test')).tap() - await element(by.id('selector')).swipe('left') - await element(by.id('selector-3')).tap() + await element(by.id('profilePager-selector')).swipe('left') + await element(by.id('profilePager-selector-3')).tap() await element(by.id('list-Good Ppl')).tap() await element(by.id('headerDropdownBtn')).tap() await element(by.text('Report List')).tap() diff --git a/__e2e__/tests/home-screen.test.ts b/__e2e__/tests/home-screen.test.ts index b6cd1919..79fca1e6 100644 --- a/__e2e__/tests/home-screen.test.ts +++ b/__e2e__/tests/home-screen.test.ts @@ -1,5 +1,7 @@ /* eslint-env detox/detox */ +import {describe, beforeAll, it} from '@jest/globals' +import {expect} from 'detox' import {openApp, loginAsAlice, createServer} from '../util' describe('Home screen', () => { diff --git a/__e2e__/tests/invite-codes.test.ts b/__e2e__/tests/invite-codes.test.ts index 124c1af9..7db7c595 100644 --- a/__e2e__/tests/invite-codes.test.ts +++ b/__e2e__/tests/invite-codes.test.ts @@ -5,6 +5,8 @@ * with the side drawer. */ +import {describe, beforeAll, it} from '@jest/globals' +import {expect} from 'detox' import {openApp, loginAsAlice, createServer} from '../util' describe('invite-codes', () => { @@ -16,7 +18,6 @@ describe('invite-codes', () => { }) it('I can fetch invite codes', async () => { - await expect(element(by.id('signInButton'))).toBeVisible() await loginAsAlice() await element(by.id('e2eOpenInviteCodesModal')).tap() await expect(element(by.id('inviteCodesModal'))).toBeVisible() @@ -27,6 +28,7 @@ describe('invite-codes', () => { }) it('I can create a new account with the invite code', async () => { + await element(by.id('e2eOpenLoggedOutView')).tap() await element(by.id('createAccountButton')).tap() await device.takeScreenshot('1- opened create account screen') await element(by.id('otherServerBtn')).tap() @@ -51,19 +53,4 @@ describe('invite-codes', () => { await element(by.id('continueBtn')).tap() await expect(element(by.id('homeScreen'))).toBeVisible() }) - - it('I get a notification for the new user', async () => { - await element(by.id('e2eSignOut')).tap() - await loginAsAlice() - await waitFor(element(by.id('homeScreen'))) - .toBeVisible() - .withTimeout(5000) - await element(by.id('bottomBarNotificationsBtn')).tap() - await expect(element(by.id('invitedUser'))).toBeVisible() - }) - - it('I can dismiss the new user notification', async () => { - await element(by.id('dismissBtn')).tap() - await expect(element(by.id('invitedUser'))).not.toBeVisible() - }) }) diff --git a/__e2e__/tests/login.test.ts b/__e2e__/tests/login.test.ts index 788016db..b4cedef6 100644 --- a/__e2e__/tests/login.test.ts +++ b/__e2e__/tests/login.test.ts @@ -1,5 +1,7 @@ /* eslint-env detox/detox */ +import {describe, beforeAll, it} from '@jest/globals' +import {expect} from 'detox' import {openApp, login, createServer} from '../util' describe('Login', () => { @@ -10,6 +12,8 @@ describe('Login', () => { }) it('As Alice, I can login', async () => { + await element(by.id('e2eOpenLoggedOutView')).tap() + await expect(element(by.id('signInButton'))).toBeVisible() await login(service, 'alice', 'hunter2', { takeScreenshots: true, diff --git a/__e2e__/tests/merge-feed.test.ts b/__e2e__/tests/merge-feed.test.skip.ts similarity index 93% rename from __e2e__/tests/merge-feed.test.ts rename to __e2e__/tests/merge-feed.test.skip.ts index 903e3432..4a8b3cbc 100644 --- a/__e2e__/tests/merge-feed.test.ts +++ b/__e2e__/tests/merge-feed.test.skip.ts @@ -1,5 +1,7 @@ /* eslint-env detox/detox */ +import {describe, beforeAll, it} from '@jest/globals' +import {expect} from 'detox' import {openApp, loginAsAlice, createServer} from '../util' describe('Mergefeed', () => { @@ -9,8 +11,12 @@ describe('Mergefeed', () => { }) it('Login', async () => { + await element(by.id('e2eOpenLoggedOutView')).tap() await loginAsAlice() await element(by.id('e2eToggleMergefeed')).tap() + await element(by.id('bottomBarFeedsBtn')).tap() + await element(by.id('feed-alice-favs-toggleSave')).tap() + await element(by.id('e2eGotoHome')).tap() }) it('Sees the expected mix of posts with default filters', async () => { diff --git a/__e2e__/tests/mod-lists.test.ts b/__e2e__/tests/mod-lists.test.ts index 2b33aaed..5280920b 100644 --- a/__e2e__/tests/mod-lists.test.ts +++ b/__e2e__/tests/mod-lists.test.ts @@ -1,5 +1,7 @@ /* eslint-env detox/detox */ +import {describe, beforeAll, it} from '@jest/globals' +import {expect} from 'detox' import {openApp, loginAsAlice, loginAsBob, createServer, sleep} from '../util' describe('Mod lists', () => { @@ -11,7 +13,6 @@ describe('Mod lists', () => { }) it('Login and view my modlists', async () => { - await expect(element(by.id('signInButton'))).toBeVisible() await loginAsAlice() await element(by.id('e2eGotoModeration')).tap() await element(by.id('moderationlistsBtn')).tap() @@ -31,7 +32,7 @@ describe('Mod lists', () => { it('Edit display name and description via the edit modlist modal', async () => { await element(by.id('headerDropdownBtn')).tap() - await element(by.text('Edit List Details')).tap() + await element(by.text('Edit list details')).tap() await expect(element(by.id('createOrEditListModal'))).toBeVisible() await element(by.id('editNameInput')).clearText() await element(by.id('editNameInput')).typeText('Bad Ppl') @@ -49,7 +50,7 @@ describe('Mod lists', () => { it('Remove description via the edit modlist modal', async () => { await element(by.id('headerDropdownBtn')).tap() - await element(by.text('Edit List Details')).tap() + await element(by.text('Edit list details')).tap() await expect(element(by.id('createOrEditListModal'))).toBeVisible() await element(by.id('editDescriptionInput')).clearText() await element(by.id('saveBtn')).tap() @@ -64,7 +65,7 @@ describe('Mod lists', () => { it('Set avi via the edit modlist modal', async () => { await expect(element(by.id('userAvatarFallback'))).toExist() await element(by.id('headerDropdownBtn')).tap() - await element(by.text('Edit List Details')).tap() + await element(by.text('Edit list details')).tap() await expect(element(by.id('createOrEditListModal'))).toBeVisible() await element(by.id('changeAvatarBtn')).tap() await element(by.text('Library')).tap() @@ -81,7 +82,7 @@ describe('Mod lists', () => { it('Remove avi via the edit modlist modal', async () => { await expect(element(by.id('userAvatarImage'))).toExist() await element(by.id('headerDropdownBtn')).tap() - await element(by.text('Edit List Details')).tap() + await element(by.text('Edit list details')).tap() await expect(element(by.id('createOrEditListModal'))).toBeVisible() await element(by.id('changeAvatarBtn')).tap() await element(by.text('Remove')).tap() @@ -131,15 +132,15 @@ describe('Mod lists', () => { await expect(element(by.id('user-warn-posts.test'))).toBeVisible() await element(by.id('user-warn-posts.test-editBtn')).tap() await expect(element(by.id('userAddRemoveListsModal'))).toBeVisible() - await element(by.id('toggleBtn-Bad Ppl')).tap() - await element(by.id('saveBtn')).tap() + await element(by.id('user-warn-posts.test-addBtn')).tap() + await element(by.id('doneBtn')).tap() await expect(element(by.id('userAddRemoveListsModal'))).not.toBeVisible() }) it('Shows the modlist on my profile', async () => { await element(by.id('bottomBarProfileBtn')).tap() - await element(by.id('selector')).swipe('left') - await element(by.id('selector-4')).tap() + await element(by.id('profilePager-selector')).swipe('left') + await element(by.id('profilePager-selector-5')).tap() await element(by.id('list-Bad Ppl')).tap() }) @@ -152,15 +153,15 @@ describe('Mod lists', () => { await element(by.id('profileHeaderDropdownBtn')).tap() await element(by.text('Add to Lists')).tap() await expect(element(by.id('userAddRemoveListsModal'))).toBeVisible() - await element(by.id('toggleBtn-Bad Ppl')).tap() - await element(by.id('saveBtn')).tap() + await element(by.id('user-bob.test-addBtn')).tap() + await element(by.id('doneBtn')).tap() await expect(element(by.id('userAddRemoveListsModal'))).not.toBeVisible() await element(by.id('profileHeaderDropdownBtn')).tap() await element(by.text('Add to Lists')).tap() await expect(element(by.id('userAddRemoveListsModal'))).toBeVisible() - await element(by.id('toggleBtn-Bad Ppl')).tap() - await element(by.id('saveBtn')).tap() + await element(by.id('user-bob.test-addBtn')).tap() + await element(by.id('doneBtn')).tap() await expect(element(by.id('userAddRemoveListsModal'))).not.toBeVisible() }) @@ -171,8 +172,8 @@ describe('Mod lists', () => { await element(by.id('bottomBarSearchBtn')).tap() await element(by.id('searchTextInput')).typeText('alice') await element(by.id('searchAutoCompleteResult-alice.test')).tap() - await element(by.id('selector')).swipe('left') - await element(by.id('selector-3')).tap() + await element(by.id('profilePager-selector')).swipe('left') + await element(by.id('profilePager-selector-3')).tap() await element(by.id('list-Bad Ppl')).tap() await element(by.id('headerDropdownBtn')).tap() await element(by.text('Report List')).tap() diff --git a/__e2e__/tests/profile-screen.test.ts b/__e2e__/tests/profile-screen.test.ts index 82722f38..b4624e45 100644 --- a/__e2e__/tests/profile-screen.test.ts +++ b/__e2e__/tests/profile-screen.test.ts @@ -1,5 +1,7 @@ /* eslint-env detox/detox */ +import {describe, beforeAll, it} from '@jest/globals' +import {expect} from 'detox' import {openApp, loginAsAlice, createServer, sleep} from '../util' describe('Profile screen', () => { @@ -11,17 +13,16 @@ describe('Profile screen', () => { }) it('Login and navigate to my profile', async () => { - await expect(element(by.id('signInButton'))).toBeVisible() await loginAsAlice() await element(by.id('bottomBarProfileBtn')).tap() }) it('Can see feeds', async () => { - await element(by.id('selector')).swipe('left') - await element(by.id('selector-4')).tap() + await element(by.id('profilePager-selector')).swipe('left') + await element(by.id('profilePager-selector-4')).tap() await expect(element(by.id('feed-alice-favs'))).toBeVisible() - await element(by.id('selector')).swipe('right') - await element(by.id('selector-0')).tap() + await element(by.id('profilePager-selector')).swipe('right') + await element(by.id('profilePager-selector-0')).tap() }) it('Open and close edit profile modal', async () => { @@ -135,6 +136,14 @@ describe('Profile screen', () => { }) it('Can like posts', async () => { + await element(by.id('postsFeed-flatlist')).swipe( + 'down', + 'slow', + 1, + 0.5, + 0.5, + ) + const posts = by.id('feedItem-by-bob.test') await expect( element(by.id('likeCount').withAncestor(posts)).atIndex(0), diff --git a/__e2e__/tests/search-screen.test.ts b/__e2e__/tests/search-screen.test.ts index 8b3f55b3..1dbb3cbf 100644 --- a/__e2e__/tests/search-screen.test.ts +++ b/__e2e__/tests/search-screen.test.ts @@ -1,5 +1,7 @@ /* eslint-env detox/detox */ +import {describe, beforeAll, it} from '@jest/globals' +import {expect} from 'detox' import {openApp, loginAsAlice, createServer} from '../util' describe('Search screen', () => { diff --git a/__e2e__/tests/self-labeling.test.ts b/__e2e__/tests/self-labeling.test.ts index 68678688..f629f91a 100644 --- a/__e2e__/tests/self-labeling.test.ts +++ b/__e2e__/tests/self-labeling.test.ts @@ -1,5 +1,7 @@ /* eslint-env detox/detox */ +import {describe, beforeAll, it} from '@jest/globals' +import {expect} from 'detox' import {openApp, loginAsAlice, createServer, sleep} from '../util' describe('Self-labeling', () => { diff --git a/__e2e__/tests/thread-muting.test.ts b/__e2e__/tests/thread-muting.test.ts index 3b2dc122..ae62f93d 100644 --- a/__e2e__/tests/thread-muting.test.ts +++ b/__e2e__/tests/thread-muting.test.ts @@ -1,5 +1,7 @@ /* eslint-env detox/detox */ +import {describe, beforeAll, it} from '@jest/globals' +import {expect} from 'detox' import {openApp, loginAsAlice, loginAsBob, createServer} from '../util' describe('Thread muting', () => { @@ -48,7 +50,7 @@ describe('Thread muting', () => { await loginAsBob() await element(by.id('bottomBarProfileBtn')).tap() - await element(by.id('selector-1')).tap() + await element(by.id('profilePager-selector-1')).tap() const bobPosts = by.id('feedItem-by-bob.test') await element(by.id('replyBtn').withAncestor(bobPosts)).atIndex(0).tap() await element(by.id('composerTextInput')).typeText('Reply 2') diff --git a/__e2e__/tests/thread-screen.test.ts b/__e2e__/tests/thread-screen.test.ts index 02831d05..e3cff12c 100644 --- a/__e2e__/tests/thread-screen.test.ts +++ b/__e2e__/tests/thread-screen.test.ts @@ -1,5 +1,7 @@ /* eslint-env detox/detox */ +import {describe, beforeAll, it} from '@jest/globals' +import {expect} from 'detox' import {openApp, loginAsAlice, createServer} from '../util' describe('Thread screen', () => { @@ -31,15 +33,15 @@ describe('Thread screen', () => { it('Can like the root post', async () => { const post = by.id('postThreadItem-by-bob.test') await expect( - element(by.id('likeCount').withAncestor(post)).atIndex(0), + element(by.id('likeCount-expanded').withAncestor(post)).atIndex(0), ).not.toExist() await element(by.id('likeBtn').withAncestor(post)).atIndex(0).tap() await expect( - element(by.id('likeCount').withAncestor(post)).atIndex(0), + element(by.id('likeCount-expanded').withAncestor(post)).atIndex(0), ).toHaveText('1 like') await element(by.id('likeBtn').withAncestor(post)).atIndex(0).tap() await expect( - element(by.id('likeCount').withAncestor(post)).atIndex(0), + element(by.id('likeCount-expanded').withAncestor(post)).atIndex(0), ).not.toExist() }) @@ -61,21 +63,21 @@ describe('Thread screen', () => { it('Can repost the root post', async () => { const post = by.id('postThreadItem-by-bob.test') await expect( - element(by.id('repostCount').withAncestor(post)).atIndex(0), + element(by.id('repostCount-expanded').withAncestor(post)).atIndex(0), ).not.toExist() await element(by.id('repostBtn').withAncestor(post)).atIndex(0).tap() await expect(element(by.id('repostModal'))).toBeVisible() await element(by.id('repostBtn').withAncestor(by.id('repostModal'))).tap() await expect(element(by.id('repostModal'))).not.toBeVisible() await expect( - element(by.id('repostCount').withAncestor(post)).atIndex(0), + element(by.id('repostCount-expanded').withAncestor(post)).atIndex(0), ).toHaveText('1 repost') await element(by.id('repostBtn').withAncestor(post)).atIndex(0).tap() await expect(element(by.id('repostModal'))).toBeVisible() await element(by.id('repostBtn').withAncestor(by.id('repostModal'))).tap() await expect(element(by.id('repostModal'))).not.toBeVisible() await expect( - element(by.id('repostCount').withAncestor(post)).atIndex(0), + element(by.id('repostCount-expanded').withAncestor(post)).atIndex(0), ).not.toExist() }) diff --git a/docs/testing.md b/docs/testing.md index 123c58eb..e9b9445e 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -1,5 +1,8 @@ # Testing instructions +Make sure you've copied `.env.example` to `.env.test` and provided any required +values. + ### Using Maestro E2E tests 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. @@ -11,4 +14,4 @@ 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) 4. The `yarn perf:test:measure` will run the `scroll.yaml` test located in `__e2e__/maestro/scroll.yaml` and give the results in `.perf/results.json` which can be viewed by running `yarn:perf:results` -5. You can also run your own tests by running `yarn perf:test ` where `` is the path to your test file. For example, `yarn perf:test __e2e__/maestro/scroll.yaml` will run the `scroll.yaml` test located in `__e2e__/maestro/scroll.yaml`. \ No newline at end of file +5. You can also run your own tests by running `yarn perf:test ` where `` is the path to your test file. For example, `yarn perf:test __e2e__/maestro/scroll.yaml` will run the `scroll.yaml` test located in `__e2e__/maestro/scroll.yaml`. diff --git a/index.js b/index.js index 4fc95bd1..7630d053 100644 --- a/index.js +++ b/index.js @@ -1,15 +1,21 @@ import 'react-native-gesture-handler' // must be first - import {LogBox} from 'react-native' -LogBox.ignoreLogs(['Require cycle:']) // suppress require-cycle warnings, it's fine import '#/platform/polyfills' +import {IS_TEST} from '#/env' import {registerRootComponent} from 'expo' import {doPolyfill} from '#/lib/api/api-polyfill' -doPolyfill() import App from '#/App' +doPolyfill() + +if (IS_TEST) { + LogBox.ignoreAllLogs() // suppress all logs in tests +} else { + LogBox.ignoreLogs(['Require cycle:']) // suppress require-cycle warnings, it's fine +} + // registerRootComponent calls AppRegistry.registerComponent('main', () => App); // It also ensures that whether you load the app in Expo Go or in a native build, // the environment is set up appropriately diff --git a/jest/test-pds.ts b/jest/test-pds.ts index bc369260..a0cb04a1 100644 --- a/jest/test-pds.ts +++ b/jest/test-pds.ts @@ -59,17 +59,21 @@ export async function createServer( ): Promise { const port = await getPort() const port2 = await getPort(port + 1) + const port3 = await getPort(port2 + 1) const pdsUrl = `http://localhost:${port}` const id = ids.next() + const testNet = await TestNetwork.create({ pds: { port, - publicUrl: pdsUrl, - inviteRequired, + hostname: 'localhost', dbPostgresSchema: `pds_${id}`, + inviteRequired, }, bsky: { dbPostgresSchema: `bsky_${id}`, + port: port3, + publicUrl: 'http://localhost:2584', }, plc: {port: port2}, }) diff --git a/package.json b/package.json index 7a6efcd0..fdca3e89 100644 --- a/package.json +++ b/package.json @@ -21,9 +21,9 @@ "lint": "eslint ./src --ext .js,.jsx,.ts,.tsx", "typecheck": "tsc --project ./tsconfig.check.json", "e2e:mock-server": "./jest/dev-infra/with-test-redis-and-db.sh ts-node __e2e__/mock-server.ts", - "e2e:metro": "RN_SRC_EXT=e2e.ts,e2e.tsx expo run:ios", - "e2e:build": "detox build -c ios.sim.debug", - "e2e:run": "detox test --configuration ios.sim.debug --take-screenshots all", + "e2e:metro": "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", "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", @@ -168,7 +168,7 @@ "zod": "^3.20.2" }, "devDependencies": { - "@atproto/dev-env": "^0.2.5", + "@atproto/dev-env": "^0.2.16", "@babel/core": "^7.23.2", "@babel/preset-env": "^7.20.0", "@babel/runtime": "^7.20.0", diff --git a/src/env.ts b/src/env.ts index 7b255e7e..fbcc15f5 100644 --- a/src/env.ts +++ b/src/env.ts @@ -1,4 +1,4 @@ -export const IS_TEST = process.env.NODE_ENV === 'test' +export const IS_TEST = process.env.EXPO_PUBLIC_ENV === 'test' export const IS_DEV = __DEV__ export const IS_PROD = !IS_DEV export const LOG_DEBUG = process.env.EXPO_PUBLIC_LOG_DEBUG || '' diff --git a/src/state/modals/index.tsx b/src/state/modals/index.tsx index 84fea3ba..94d6d9ec 100644 --- a/src/state/modals/index.tsx +++ b/src/state/modals/index.tsx @@ -61,6 +61,7 @@ export interface CreateOrEditListModal { export interface UserAddRemoveListsModal { name: 'user-add-remove-lists' subject: string + handle: string displayName: string onAdd?: (listUri: string) => void onRemove?: (listUri: string) => void diff --git a/src/view/com/feeds/FeedSourceCard.tsx b/src/view/com/feeds/FeedSourceCard.tsx index 1f2af069..460816fc 100644 --- a/src/view/com/feeds/FeedSourceCard.tsx +++ b/src/view/com/feeds/FeedSourceCard.tsx @@ -170,6 +170,7 @@ export function FeedSourceCardLoaded({ {showSaveBtn && feed.type === 'feed' && ( void onRemove?: (listUri: string) => void @@ -60,6 +62,7 @@ export function Component({ list={list} memberships={memberships} subject={subject} + handle={handle} onAdd={onAdd} onRemove={onRemove} /> @@ -87,6 +90,7 @@ function ListItem({ list, memberships, subject, + handle, onAdd, onRemove, }: { @@ -94,6 +98,7 @@ function ListItem({ list: GraphDefs.ListView memberships: ListMembersip[] | undefined subject: string + handle: string onAdd?: (listUri: string) => void onRemove?: (listUri: string) => void }) { @@ -182,7 +187,7 @@ function ListItem({ ) : (