diff --git a/.github/workflows/template-connect-popup-test-params.yml b/.github/workflows/template-connect-popup-test-params.yml index 5efcf459942b..f94318fc3523 100644 --- a/.github/workflows/template-connect-popup-test-params.yml +++ b/.github/workflows/template-connect-popup-test-params.yml @@ -21,6 +21,11 @@ on: type: "boolean" required: false default: true + run-core-in-popup: + description: "Flag to indicate whether to run the core-in-popup job" + type: "boolean" + required: false + default: false build-overview: description: "Flag to indicate whether to build connect-popup-overview.html" type: "boolean" @@ -68,7 +73,7 @@ jobs: uses: actions/upload-artifact@v4 if: ${{ inputs.build-overview }} with: - name: static-overview-${{ inputs.test-name }}-${{ github.run_attempt }} + name: core-in-popup-static-overview-${{ inputs.test-name }}-${{ github.run_attempt }} path: | tmp_overview_directory/ @@ -146,3 +151,64 @@ jobs: echo "Tests failed" exit 1 fi + + core_in_popup: + name: core_in_popup + runs-on: ubuntu-latest + if: ${{ inputs.run-core-in-popup }} + steps: + - uses: actions/checkout@v4 + with: + submodules: true + + - name: Extract branch name + run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT + id: extract_branch + + - name: Install dependencies + run: | + echo -e "\nenableScripts: false" >> .yarnrc.yml + yarn workspaces focus @trezor/connect-popup + + - name: Run connect popup test + env: + URL: https://${{ inputs.DEV_SERVER_HOSTNAME }}/connect/${{ steps.extract_branch.outputs.branch }}/?core-in-popup=true + CORE_IN_POPUP: true + # skip settings page, this url is set at build time anyway + #TREZOR_CONNECT_SRC: https://${{ inputs.DEV_SERVER_HOSTNAME }}/connect/${{ steps.extract_branch.outputs.branch }}/ + CI_COMMIT_BRANCH: ${{ steps.extract_branch.outputs.branch }} + CI_JOB_NAME: ${{ inputs.test-name }}-${{ github.run_attempt }} + run: | + ./docker/docker-connect-popup-ci.sh ${{ inputs.test-name }} + + - name: Prepare static overview + if: ${{ inputs.build-overview }} + run: | + echo "Preparing static overview" + mkdir -p tmp_overview_directory + cp -R ./packages/connect-popup/e2e/screenshots/* tmp_overview_directory/ + cp packages/connect-popup/connect-popup-overview.html tmp_overview_directory/connect-popup-overview.html + + - name: Upload static overview artifact + uses: actions/upload-artifact@v4 + if: ${{ inputs.build-overview }} + with: + name: static-overview-${{ inputs.test-name }}-${{ github.run_attempt }} + path: | + tmp_overview_directory/ + + - name: Upload artifacts + if: failure() + uses: actions/upload-artifact@v4 + with: + name: core-in-popup-test-artifacts-${{ inputs.test-name }}-${{ github.run_attempt }}-${{ github.run_id }} + path: | + packages/connect-popup/test-results + + - name: Check Test Success + run: | + # If there is `test-results` it means it has failed. + if [ -f "packages/connect-popup/test-results" ]; then + echo "Tests failed" + exit 1 + fi diff --git a/.github/workflows/test-connect-popup.yml b/.github/workflows/test-connect-popup.yml index 5a6dad786e69..cd87b986d79d 100644 --- a/.github/workflows/test-connect-popup.yml +++ b/.github/workflows/test-connect-popup.yml @@ -70,6 +70,7 @@ jobs: test-name: methods.test DEV_SERVER_HOSTNAME: dev.suite.sldev.cz run-webextension: ${{ github.event_name == 'schedule' }} + run-core-in-popup: true build-overview: true popup-close: @@ -79,6 +80,7 @@ jobs: test-name: popup-close.test DEV_SERVER_HOSTNAME: dev.suite.sldev.cz run-webextension: true + run-core-in-popup: true passphrase: needs: [build-deploy] @@ -87,6 +89,7 @@ jobs: test-name: passphrase.test DEV_SERVER_HOSTNAME: dev.suite.sldev.cz run-webextension: true + run-core-in-popup: true popup-pages: needs: [build-deploy] diff --git a/docker/docker-compose.connect-popup-ci.yml b/docker/docker-compose.connect-popup-ci.yml index 6d0a1a0a4628..daed8d3104c8 100644 --- a/docker/docker-compose.connect-popup-ci.yml +++ b/docker/docker-compose.connect-popup-ci.yml @@ -22,6 +22,7 @@ services: - CI_JOB_NAME=$CI_JOB_NAME - TEST_FILE=$TEST_FILE - IS_WEBEXTENSION=$IS_WEBEXTENSION + - CORE_IN_POPUP=$CORE_IN_POPUP - TREZOR_CONNECT_SRC=$TREZOR_CONNECT_SRC working_dir: /e2e command: bash -c "npx playwright install && yarn workspace @trezor/connect-popup test:e2e $TEST_FILE" diff --git a/docker/docker-compose.connect-popup-test.yml b/docker/docker-compose.connect-popup-test.yml index e04908083f54..ed96e165b8a1 100644 --- a/docker/docker-compose.connect-popup-test.yml +++ b/docker/docker-compose.connect-popup-test.yml @@ -33,6 +33,7 @@ services: - PWDEBUG=console - TEST_FILE=$TEST_FILE - IS_WEBEXTENSION=$IS_WEBEXTENSION + - CORE_IN_POPUP=$CORE_IN_POPUP - TREZOR_CONNECT_SRC=$TREZOR_CONNECT_SRC working_dir: /trezor-suite command: bash -c "docker/wait-for-200.sh http://localhost:8088/index.html && yarn workspace @trezor/connect-popup test:e2e $TEST_FILE" diff --git a/docker/docker-connect-popup-ci.sh b/docker/docker-connect-popup-ci.sh index 1bcb133eab5b..fc50d9d366a0 100755 --- a/docker/docker-connect-popup-ci.sh +++ b/docker/docker-connect-popup-ci.sh @@ -6,6 +6,7 @@ export LOCAL_USER_ID export TEST_FILE=$1 export URL=$URL export TREZOR_CONNECT_SRC=$TREZOR_CONNECT_SRC +export CORE_IN_POPUP=$CORE_IN_POPUP export IS_WEBEXTENSION=$IS_WEBEXTENSION docker compose -f ./docker/docker-compose.connect-popup-ci.yml up --build --abort-on-container-exit diff --git a/docker/docker-connect-popup-test.sh b/docker/docker-connect-popup-test.sh index 208e428e4b4a..a1105f6979fc 100755 --- a/docker/docker-connect-popup-test.sh +++ b/docker/docker-connect-popup-test.sh @@ -6,5 +6,7 @@ LOCAL_USER_ID="$(id -u "$USER")" export LOCAL_USER_ID export TEST_FILE=$1 export TREZOR_CONNECT_SRC=http://localhost:8088/ +export CORE_IN_POPUP=$CORE_IN_POPUP +export IS_WEBEXTENSION=$IS_WEBEXTENSION docker compose -f ./docker/docker-compose.connect-popup-test.yml up --build --abort-on-container-exit diff --git a/packages/connect-explorer/src/actions/trezorConnectActions.ts b/packages/connect-explorer/src/actions/trezorConnectActions.ts index 963a78038e4e..26c579406e7d 100644 --- a/packages/connect-explorer/src/actions/trezorConnectActions.ts +++ b/packages/connect-explorer/src/actions/trezorConnectActions.ts @@ -90,9 +90,12 @@ export const init = console.log('using @trezor/connect hosted on: ', window.__TREZOR_CONNECT_SRC); } + // Get default useCoreInPopup from URL params (?core-in-popup=true) + const urlParams = new URLSearchParams(window.location.search); + const useCoreInPopup = urlParams.get('core-in-popup') === 'true'; + const connectOptions = { - // Example with core mode enabled - useCoreInPopup: true, + useCoreInPopup, transportReconnect: true, popup: true, debug: true, diff --git a/packages/connect-popup/e2e/support/helpers.ts b/packages/connect-popup/e2e/support/helpers.ts index 4f5f5850c3f1..b548aee390ae 100644 --- a/packages/connect-popup/e2e/support/helpers.ts +++ b/packages/connect-popup/e2e/support/helpers.ts @@ -160,6 +160,9 @@ export const setConnectSettings = async ( connectSrc, ); } + if (process.env.CORE_IN_POPUP) { + await waitAndClick(explorerPage, ['@checkbox/useCoreInPopup']); + } await waitAndClick(explorerPage, ['@submit-button']); }; diff --git a/packages/connect-popup/e2e/tests/methods.test.ts b/packages/connect-popup/e2e/tests/methods.test.ts index 3c8394179d16..8e7d2fd8ebd5 100644 --- a/packages/connect-popup/e2e/tests/methods.test.ts +++ b/packages/connect-popup/e2e/tests/methods.test.ts @@ -15,6 +15,7 @@ let device = {}; let context: any = null; const isWebExtension = process.env.IS_WEBEXTENSION === 'true'; +const isCoreInPopup = process.env.CORE_IN_POPUP === 'true'; const screenshotEmu = async (path: string) => { const { response } = await TrezorUserEnvLink.send({ @@ -133,7 +134,7 @@ filteredFixtures.forEach(f => { }); await popup.click("button[data-test='@analytics/continue-button']"); - if (isWebExtension) { + if (isWebExtension || isCoreInPopup) { log(f.url, 'waiting for select device'); await popup.waitForSelector('.select-device-list button.list', { state: 'visible' }); await popup.click('.select-device-list button.list'); diff --git a/packages/connect-popup/e2e/tests/passphrase.test.ts b/packages/connect-popup/e2e/tests/passphrase.test.ts index 42dfdbfbed77..c1f04e53454e 100644 --- a/packages/connect-popup/e2e/tests/passphrase.test.ts +++ b/packages/connect-popup/e2e/tests/passphrase.test.ts @@ -14,6 +14,8 @@ const url = process.env.URL || 'http://localhost:8088/'; const bridgeVersion = '2.0.31'; const isWebExtension = process.env.IS_WEBEXTENSION === 'true'; +const isCoreInPopup = process.env.CORE_IN_POPUP === 'true'; +const skipCheck = isWebExtension || isCoreInPopup; const connectSrc = process.env.TREZOR_CONNECT_SRC; let context: any = null; @@ -102,7 +104,7 @@ test('input passphrase in popup and device accepts it', async () => { log('clicking on analytics continue button'); await waitAndClick(popup, ['@analytics/continue-button']); - if (isWebExtension) { + if (isWebExtension || isCoreInPopup) { log('waiting for device list'); await popup.waitForSelector('.select-device-list button.list'); await popup.click('.select-device-list button.list'); @@ -149,7 +151,7 @@ test('introduce passphrase in popup and device rejects it', async () => { } await waitAndClick(popup, ['@analytics/continue-button']); - if (isWebExtension) { + if (isWebExtension || isCoreInPopup) { log('waiting for device list'); await popup.waitForSelector('.select-device-list button.list'); await popup.click('.select-device-list button.list'); @@ -174,7 +176,7 @@ test('introduce passphrase in popup and device rejects it', async () => { }); test('introduce passphrase successfully next time should not ask for it', async () => { - test.skip(isWebExtension, 'test does not apply for webextension'); + test.skip(skipCheck, 'test does not apply for webextension'); log(`test: ${test.info().title}`); @@ -220,7 +222,7 @@ test('introduce passphrase successfully next time should not ask for it', async }); test('introduce passphrase successfully reload 3rd party it should ask again for passphrase', async () => { - test.skip(isWebExtension, 'test does not apply for webextension'); + test.skip(skipCheck, 'test does not apply for webextension'); log(`test: ${test.info().title}`); @@ -270,7 +272,7 @@ test('introduce passphrase successfully reload 3rd party it should ask again for test('passphrase mismatch', async ({ page }) => { // This test uses addScriptTag so we cannot run it in web extension. - test.skip(isWebExtension); + test.skip(skipCheck); log(`test: ${test.info().title}`); log('start', test.info().title); @@ -356,6 +358,18 @@ test('passphrase mismatch', async ({ page }) => { [popup] = await Promise.all([page.waitForEvent('popup')]); + // Acquire device if not acquired + try { + log('handling if unacquired'); + await popup.waitForSelector('.explain.unacquired', { + state: 'visible', + timeout: 10000, + }); + await popup.click('.explain.unacquired'); + } catch (error) { + // May appear or not + } + log('waiting and click confirm permissions button'); await waitAndClick(popup, ['@permissions/confirm-button', '@export-address/confirm-button']); diff --git a/packages/connect-popup/e2e/tests/popup-close.test.ts b/packages/connect-popup/e2e/tests/popup-close.test.ts index ab22b016c7cc..8a6884a46b4c 100644 --- a/packages/connect-popup/e2e/tests/popup-close.test.ts +++ b/packages/connect-popup/e2e/tests/popup-close.test.ts @@ -16,6 +16,8 @@ import { const url = process.env.URL || 'http://localhost:8088/'; const isWebExtension = process.env.IS_WEBEXTENSION === 'true'; +const isCoreInPopup = process.env.CORE_IN_POPUP === 'true'; +const skipCheck = isWebExtension || isCoreInPopup; const connectSrc = process.env.TREZOR_CONNECT_SRC; const WAIT_AFTER_TEST = 3000; // how long test should wait for more potential trezord requests @@ -62,7 +64,7 @@ const setup = async ({ page, context }: { page: Page; context?: BrowserContext } explorerUrl = contexts.explorerUrl; const logPage = await browserContext!.newPage(); - await logPage.goto(`${url}log.html`); + await logPage.goto(formatUrl(url, 'log.html')); await setConnectSettings( explorerPage, @@ -100,7 +102,7 @@ const setup = async ({ page, context }: { page: Page; context?: BrowserContext } log('beforeEach', 'waiting for popup load state'); await popup.waitForLoadState('load'); - if (isWebExtension) { + if (isWebExtension || isCoreInPopup) { log('beforeEach', 'waiting for select device'); await popup.waitForSelector('.select-device-list button.list', { state: 'visible' }); await popup.click('.select-device-list button.list'); @@ -224,6 +226,11 @@ test('when user cancels permissions in popup it closes automatically', async ({ await popup.waitForLoadState('load'); + if (isCoreInPopup) { + await popup.waitForSelector('.select-device-list button.list', { state: 'visible' }); + await popup.click('.select-device-list button.list'); + } + await popup.waitForSelector('button.confirm', { state: 'visible', timeout: 40000 }); await popup.waitForSelector("button[data-test='@permissions/confirm-button']"); // We are testing that when cancel permissions, popup is closed automatically. @@ -234,7 +241,7 @@ test('when user cancels permissions in popup it closes automatically', async ({ test('device dialogue cancelled IN POPUP by user', async ({ page, context }) => { // TODO: this test should also work with webextension and for some reason it does not work in CI but it works locally. - test.skip(isWebExtension, 'todo: skip for now'); + test.skip(skipCheck, 'todo: skip for now'); log(`test: ${test.info().title}`); await setup({ page, context }); @@ -276,7 +283,7 @@ test('popup should close and open new one when popup is in error state and user context, }) => { // TODO: this test should also work with webextension and for some reason it does not work in CI but it works locally. - test.skip(isWebExtension, 'todo: skip for now'); + test.skip(skipCheck, 'todo: skip for now'); log(`test: ${test.info().title}`); await setup({ page, context }); @@ -309,7 +316,7 @@ test('popup should be focused when a call is in progress and user triggers new c context, }) => { // TODO: this test should also work with webextension and for some reason it does not work in CI but it works locally. - test.skip(isWebExtension, 'todo: skip for now'); + test.skip(skipCheck, 'todo: skip for now'); log(`test: ${test.info().title}`); await setup({ page, context }); @@ -356,7 +363,7 @@ test('popup should be focused when a call is in progress and user triggers new c test('popup should close when third party is closed', async ({ page, context }) => { // This test should be skipped in webextension with service-worker, due to the fact that in that case // that serviceworker is persistent and does not necessarily has to be over if the page that initiated the call is closed. - test.skip(isWebExtension, 'test does not apply for webextension'); + test.skip(skipCheck, 'test does not apply for webextension'); log(`test: ${test.info().title}`); await setup({ page, context }); diff --git a/packages/connect-popup/e2e/tests/popup-pages.test.ts b/packages/connect-popup/e2e/tests/popup-pages.test.ts index 4d7aa7a1af7a..07baf253729a 100644 --- a/packages/connect-popup/e2e/tests/popup-pages.test.ts +++ b/packages/connect-popup/e2e/tests/popup-pages.test.ts @@ -136,10 +136,11 @@ test('log page should contain logs from shared worker', async ({ page, context } // Open new tab to go to log.html const logsPage = await persistentContext.newPage(); - log(`go to: ${url}log.html`); - await logsPage.goto(`${url}log.html`); + const logsUrl = `${url.split('?')[0]}log.html`; + log(`go to: ${logsUrl}`); + await logsPage.goto(logsUrl); await logsPage.waitForLoadState('load'); - log(`loaded: ${url}log.html`); + log(`loaded: ${logsUrl}`); log('waiting for download-button to be visible'); await logsPage.waitForSelector("button[data-test='@log-container/download-button']", { diff --git a/packages/connect-popup/src/index.tsx b/packages/connect-popup/src/index.tsx index 46e09e7beb08..8544a43db9e9 100644 --- a/packages/connect-popup/src/index.tsx +++ b/packages/connect-popup/src/index.tsx @@ -59,6 +59,8 @@ const escapeHtml = (payload: any) => { }; export const handleUIAffectingMessage = (message: CoreEventMessage) => { + // WIP debug + console.log('popup UI message', message.type, message.payload); switch (message.type) { case POPUP.METHOD_INFO: setState({ diff --git a/packages/connect-web/src/index.ts b/packages/connect-web/src/index.ts index d27cd0e7b3ee..9001ee4ba2c3 100644 --- a/packages/connect-web/src/index.ts +++ b/packages/connect-web/src/index.ts @@ -26,7 +26,6 @@ const selectMethodImpl = const TrezorConnect = factory({ init: (settings: Partial = {}) => { - console.log('TrezorConnect.init', settings); useCoreInPopup = settings?.useCoreInPopup === true; return selectMethodImpl('init')(settings);