diff --git a/.changelog/1768.internal.md b/.changelog/1768.internal.md new file mode 100644 index 0000000000..9383cd5c92 --- /dev/null +++ b/.changelog/1768.internal.md @@ -0,0 +1 @@ +Refactor extension tests diff --git a/playwright/tests/extension.spec.ts b/playwright/tests/extension.spec.ts index e315bf3e57..2c4e47f333 100644 --- a/playwright/tests/extension.spec.ts +++ b/playwright/tests/extension.spec.ts @@ -1,60 +1,25 @@ -import { test as base, expect, BrowserContext, chromium } from '@playwright/test' -import path from 'path' +import { test } from '../utils/extensionTestExtend' +import { expect } from '@playwright/test' import { warnSlowApi } from '../utils/warnSlowApi' import { mockApi } from '../utils/mockApi' import { expectNoErrorsInConsole } from '../utils/expectNoErrorsInConsole' import { fillPrivateKeyWithoutPassword } from '../utils/fillPrivateKey' import { privateKey, privateKeyAddress } from '../../src/utils/__fixtures__/test-inputs' -// Test dev build by default, but also allow testing production -const extensionPath = path.join(__dirname, '..', process.env.EXTENSION_PATH ?? '../build-dev/') - -// eslint-disable-next-line @typescript-eslint/no-var-requires -const extensionManifest = require(path.join(extensionPath, '/manifest.json')) -const popupFile = extensionManifest.browser_action.default_popup - -// From https://playwright.dev/docs/chrome-extensions -export const test = base.extend<{ - context: BrowserContext - extensionId: string -}>({ - // eslint-disable-next-line no-empty-pattern - context: async ({}, use) => { - const context = await chromium.launchPersistentContext('', { - headless: false, - args: [`--disable-extensions-except=${extensionPath}`, `--load-extension=${extensionPath}`], - }) - await use(context) - await context.close() - }, - extensionId: async ({ context }, use) => { - // for manifest v2: - let [background] = context.backgroundPages() - if (!background) background = await context.waitForEvent('backgroundpage') - - // for manifest v3: - // let [background] = context.serviceWorkers() - // if (!background) background = await context.waitForEvent('serviceworker') - - const extensionId = background.url().split('/')[2] - await use(extensionId) - }, -}) - test.beforeEach(async ({ context }) => { await warnSlowApi(context) await mockApi(context, 0) }) test.describe('The extension popup should load', () => { - test('should successfully load javascript chunks', async ({ page, extensionId }) => { - await page.goto(`chrome-extension://${extensionId}/${popupFile}`) + test('should successfully load javascript chunks', async ({ page, extensionPopupURL }) => { + await page.goto(`${extensionPopupURL}/`) await expect(page.getByRole('link', { name: /Open wallet/i })).toBeVisible() await expect(page.getByRole('link', { name: /Create wallet/i })).toBeVisible() }) - test('get state from background page through webext-redux', async ({ page, extensionId }) => { - await page.goto(`chrome-extension://${extensionId}/${popupFile}`) + test('get state from background page through webext-redux', async ({ page, extensionPopupURL }) => { + await page.goto(`${extensionPopupURL}/`) await page.getByRole('button', { name: /Dark mode/i }).click() await page.getByRole('button', { name: /Light mode/i }).click() @@ -62,8 +27,8 @@ test.describe('The extension popup should load', () => { await expect(page.getByTestId('mnemonic-grid').locator('> *')).toHaveCount(24) }) - test('ask for USB permissions in ledger popup', async ({ page, context, extensionId }) => { - await page.goto(`chrome-extension://${extensionId}/${popupFile}#/open-wallet`) + test('ask for USB permissions in ledger popup', async ({ page, context, extensionPopupURL }) => { + await page.goto(`${extensionPopupURL}/open-wallet`) const popupPromise = context.waitForEvent('page') await page.getByRole('button', { name: /Grant access to your Ledger/i }).click() const popup = await popupPromise @@ -74,7 +39,7 @@ test.describe('The extension popup should load', () => { await expect(popup.getByText('error').or(popup.getByText('fail'))).toBeHidden() }) - test('should allow embedded Transak widget', async ({ page, extensionId }) => { + test('should allow embedded Transak widget', async ({ page, extensionPopupURL }) => { await expectNoErrorsInConsole(page, { ignoreError: msg => { // Odd errors inside Transak @@ -82,7 +47,7 @@ test.describe('The extension popup should load', () => { if (msg.text().includes('`sessionKey` is a required property')) return true }, }) - await page.goto(`chrome-extension://${extensionId}/${popupFile}#/open-wallet/private-key`) + await page.goto(`${extensionPopupURL}/open-wallet/private-key`) await fillPrivateKeyWithoutPassword(page, { privateKey: privateKey, privateKeyAddress: privateKeyAddress, diff --git a/playwright/tests/migrating-persisted-state.spec.ts b/playwright/tests/migrating-persisted-state.spec.ts index 6ab6fc8bdd..89247a05a2 100644 --- a/playwright/tests/migrating-persisted-state.spec.ts +++ b/playwright/tests/migrating-persisted-state.spec.ts @@ -2,14 +2,14 @@ import { test, expect } from '@playwright/test' import { mockApi } from '../utils/mockApi' import { warnSlowApi } from '../utils/warnSlowApi' import { expectNoFatal } from '../utils/expectNoFatal' -import { addPersistedStorage, clearPersistedStorage } from '../utils/storage' +import { addPersistedStorageV1, clearPersistedStorage } from '../utils/storage' import { password, privateKeyUnlockedState } from '../../src/utils/__fixtures__/test-inputs' import { RootState } from '../../src/types/RootState' test.beforeEach(async ({ context, page }) => { await warnSlowApi(context) await mockApi(context, 0) - await clearPersistedStorage(page) + await clearPersistedStorage(page, '/app.webmanifest') }) test.afterEach(async ({ context }, testInfo) => { @@ -18,7 +18,7 @@ test.afterEach(async ({ context }, testInfo) => { test.describe('Migrating persisted state', () => { test('Decrypting V1 state should result in valid RootState', async ({ context, page }) => { - await addPersistedStorage(page) + await addPersistedStorageV1(page, '/app.webmanifest') await page.goto('/') await page.getByPlaceholder('Enter your password here').fill(password) await page.keyboard.press('Enter') diff --git a/playwright/tests/persist.spec.ts b/playwright/tests/persist.spec.ts index 6dfa11f322..cf10af2bc1 100644 --- a/playwright/tests/persist.spec.ts +++ b/playwright/tests/persist.spec.ts @@ -2,7 +2,7 @@ import { test, expect, Page } from '@playwright/test' import { mockApi } from '../utils/mockApi' import { warnSlowApi } from '../utils/warnSlowApi' import { expectNoFatal } from '../utils/expectNoFatal' -import { addPersistedStorage, clearPersistedStorage } from '../utils/storage' +import { addPersistedStorageV1, clearPersistedStorage } from '../utils/storage' import { mnemonicAddress0, mnemonic, @@ -18,7 +18,7 @@ import { fillPrivateKeyWithoutPassword, fillPrivateKeyAndPassword } from '../uti test.beforeEach(async ({ context, page }) => { await warnSlowApi(context) await mockApi(context, 0) - await clearPersistedStorage(page) + await clearPersistedStorage(page, '/app.webmanifest') }) test.afterEach(async ({ context }, testInfo) => { @@ -139,7 +139,7 @@ test.describe('Persist', () => { context, page, }) => { - await addPersistedStorage(page) + await addPersistedStorageV1(page, '/app.webmanifest') await page.goto('/') await mockApi(context, 123) await page.getByPlaceholder('Enter your password here').fill(password) @@ -182,7 +182,7 @@ test.describe('Persist', () => { }) test('Should NOT persist changes after user skips unlocking', async ({ page }) => { - await addPersistedStorage(page) + await addPersistedStorageV1(page, '/app.webmanifest') await page.goto('/') await page.getByRole('button', { name: /Continue without the profile/ }).click() await page.getByRole('button', { name: /Open wallet/ }).click() @@ -202,7 +202,7 @@ test.describe('Persist', () => { }) test('Should NOT crash after quickly locking a wallet', async ({ page }) => { - await addPersistedStorage(page) + await addPersistedStorageV1(page, '/app.webmanifest') await page.goto('/') await page.getByRole('button', { name: /Continue without the profile/ }).click() await page.getByRole('button', { name: /Unlock profile/ }).click() @@ -219,7 +219,7 @@ test.describe('Persist', () => { }) test('deleting stored', async ({ page }) => { - await addPersistedStorage(page) + await addPersistedStorageV1(page, '/app.webmanifest') await page.goto('/') await testDeletingAndCreatingNew(page) }) @@ -243,7 +243,7 @@ test.describe('Persist', () => { }) test('Password should not be cached in input field', async ({ page }) => { - await addPersistedStorage(page) + await addPersistedStorageV1(page, '/app.webmanifest') await page.goto('/') await page.getByPlaceholder('Enter your password here').fill(password) await page.keyboard.press('Enter') diff --git a/playwright/tests/syncTabs.spec.ts b/playwright/tests/syncTabs.spec.ts index 9468e28600..92ac42e364 100644 --- a/playwright/tests/syncTabs.spec.ts +++ b/playwright/tests/syncTabs.spec.ts @@ -10,14 +10,14 @@ import { privateKey2AddressPretty, mnemonic, } from '../../src/utils/__fixtures__/test-inputs' -import { addPersistedStorage, clearPersistedStorage } from '../utils/storage' +import { addPersistedStorageV1, clearPersistedStorage } from '../utils/storage' import { fillPrivateKeyWithoutPassword, fillPrivateKeyAndPassword } from '../utils/fillPrivateKey' import type { AccountsRow } from '../../src/vendors/oasisscan/index' test.beforeEach(async ({ context, page }) => { await warnSlowApi(context) await mockApi(context, 0) - await clearPersistedStorage(page) + await clearPersistedStorage(page, '/app.webmanifest') }) test.afterEach(async ({ context }, testInfo) => { @@ -50,7 +50,7 @@ test.describe('syncTabs', () => { }) test('incognito', async ({ page, context }) => { - await addPersistedStorage(page) + await addPersistedStorageV1(page, '/app.webmanifest') await page.goto('/') await page.getByRole('button', { name: 'Continue without the profile' }).click() await expect(page.getByTestId('account-selector')).toBeHidden() @@ -103,7 +103,7 @@ test.describe('syncTabs', () => { }) test('persisted', async ({ page, context }) => { - await addPersistedStorage(page) + await addPersistedStorageV1(page, '/app.webmanifest') await page.goto('/') await page.getByPlaceholder('Enter your password here').fill(password) await page.keyboard.press('Enter') @@ -112,7 +112,7 @@ test.describe('syncTabs', () => { }) test('incognito', async ({ page, context }) => { - await addPersistedStorage(page) + await addPersistedStorageV1(page, '/app.webmanifest') await page.goto('/') await page.getByRole('button', { name: 'Continue without the profile' }).click() const tab2 = await context.newPage() @@ -144,7 +144,7 @@ test.describe('syncTabs', () => { test.describe('adding and removing contacts in tabs', () => { test('persisted', async ({ page, context }) => { - await addPersistedStorage(page) + await addPersistedStorageV1(page, '/app.webmanifest') await page.goto('/') await page.getByPlaceholder('Enter your password here').fill(password) await page.keyboard.press('Enter') @@ -208,7 +208,7 @@ test.describe('syncTabs', () => { }) test('persisted', async ({ page, context }) => { - await addPersistedStorage(page) + await addPersistedStorageV1(page, '/app.webmanifest') await page.goto('/') await page.getByPlaceholder('Enter your password here').fill(password) await page.keyboard.press('Enter') @@ -217,7 +217,7 @@ test.describe('syncTabs', () => { }) test('incognito', async ({ page, context }) => { - await addPersistedStorage(page) + await addPersistedStorageV1(page, '/app.webmanifest') await page.goto('/') await page.getByRole('button', { name: 'Continue without the profile' }).click() const tab2 = await context.newPage() @@ -300,7 +300,7 @@ test.describe('syncTabs', () => { page, context, }) => { - await addPersistedStorage(page) + await addPersistedStorageV1(page, '/app.webmanifest') await page.goto('/') await page.getByPlaceholder('Enter your password here').fill(password) await page.keyboard.press('Enter') diff --git a/playwright/utils/extensionTestExtend.ts b/playwright/utils/extensionTestExtend.ts new file mode 100644 index 0000000000..d4659766d7 --- /dev/null +++ b/playwright/utils/extensionTestExtend.ts @@ -0,0 +1,41 @@ +import { test as base, BrowserContext, chromium } from '@playwright/test' +import path from 'path' + +// Test dev build by default, but also allow testing production +const extensionPath = path.join(__dirname, '..', process.env.EXTENSION_PATH ?? '../build-dev/') + +// eslint-disable-next-line @typescript-eslint/no-var-requires +const extensionManifest = require(path.join(extensionPath, '/manifest.json')) +const popupFile = extensionManifest.browser_action.default_popup + +// From https://playwright.dev/docs/chrome-extensions +export const test = base.extend<{ + context: BrowserContext + extensionId: string + extensionPopupURL: `chrome-extension://${string}` +}>({ + // eslint-disable-next-line no-empty-pattern + context: async ({}, use) => { + const context = await chromium.launchPersistentContext('', { + headless: false, + args: [`--disable-extensions-except=${extensionPath}`, `--load-extension=${extensionPath}`], + }) + await use(context) + await context.close() + }, + extensionId: async ({ context }, use) => { + // for manifest v2: + let [background] = context.backgroundPages() + if (!background) background = await context.waitForEvent('backgroundpage') + + // for manifest v3: + // let [background] = context.serviceWorkers() + // if (!background) background = await context.waitForEvent('serviceworker') + + const extensionId = background.url().split('/')[2] + await use(extensionId) + }, + extensionPopupURL: async ({ extensionId }, use) => { + await use(`chrome-extension://${extensionId}/${popupFile}#`) + }, +}) diff --git a/playwright/utils/storage.ts b/playwright/utils/storage.ts index 1f88559772..b8e9ff0cca 100644 --- a/playwright/utils/storage.ts +++ b/playwright/utils/storage.ts @@ -1,15 +1,21 @@ import type { Page } from '@playwright/test' import { privateKeyPersistedState } from '../../src/utils/__fixtures__/test-inputs' -export async function clearPersistedStorage(page: Page) { +export async function clearPersistedStorage( + page: Page, + url: '/app.webmanifest' | `chrome-extension://${string}`, +) { // Move to the right domain, but don't needlessly load HTML and JS. - await page.goto('/app.webmanifest') + await page.goto(url) await page.evaluate(() => window.localStorage.clear()) } -export async function addPersistedStorage(page: Page) { +export async function addPersistedStorageV1( + page: Page, + url: '/app.webmanifest' | `chrome-extension://${string}`, +) { // Move to the right domain, but don't needlessly load HTML and JS. - await page.goto('/app.webmanifest') + await page.goto(url) await page.evaluate( ([privateKeyPersistedState]) => { window.localStorage.setItem('oasis_wallet_persist_v1', privateKeyPersistedState)