From aae9ffcdae4624dda514bffc18e4f21a4a15d829 Mon Sep 17 00:00:00 2001 From: Oleksander Piskun Date: Mon, 21 Sep 2026 09:35:12 +0000 Subject: [PATCH] test: cover the page that closes the authentication popup The popup page is the one frontend of the app that no test loaded, and CI builds the frontend without running it, so a dependency update could break the end of the OAuth flow with every check green. The new test opens the page with a user name, pretends to be the window that opened it, and expects the name to arrive in the message the bundle posts back. The server container also gets ten minutes to start instead of five, after a run failed on a slow runner while the container was still booting. Signed-off-by: Oleksander Piskun --- playwright.config.ts | 3 +- playwright/e2e/oauth-popup.spec.ts | 52 ++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 playwright/e2e/oauth-popup.spec.ts diff --git a/playwright.config.ts b/playwright.config.ts index 13f2f15..94f9353 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -49,6 +49,7 @@ export default defineConfig({ reuseExistingServer: !process.env.CI, stdout: 'pipe', stderr: 'pipe', - timeout: 5 * 60 * 1000, + // the shallow server image sometimes needs several minutes to boot on a slow runner + timeout: 10 * 60 * 1000, }, }) diff --git a/playwright/e2e/oauth-popup.spec.ts b/playwright/e2e/oauth-popup.spec.ts new file mode 100644 index 0000000..df75ffa --- /dev/null +++ b/playwright/e2e/oauth-popup.spec.ts @@ -0,0 +1,52 @@ +/** + * SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { login } from '@nextcloud/e2e-test-server/playwright' +import { test as base, expect } from '@playwright/test' + +// the test container always has this admin user +const admin = { userId: 'admin', password: 'admin' } + +// Every test also fails on an uncaught exception, or on an unexpected failing request to one of the app's own routes. +// Errors of other apps on the instance are ignored on purpose. +const test = base.extend<{ appErrors: void }>({ + appErrors: [async ({ page }, use) => { + const errors: string[] = [] + page.on('pageerror', (error) => errors.push(`uncaught: ${error.message}`)) + page.on('response', (response) => { + if (response.status() >= 400 && response.url().includes('/integration_onedrive/')) { + errors.push(`${response.status()} ${response.request().method()} ${new URL(response.url()).pathname}`) + } + }) + await use() + expect(errors).toEqual([]) + }, { auto: true }], +}) + +test.beforeEach(async ({ page }) => { + await login(page.request, admin) +}) + +test.describe('OAuth popup', () => { + test('load the page that closes the authentication popup', async ({ page }) => { + // the page runs the popupSuccess bundle, which passes the account name to the window that opened it + const messages: string[] = [] + await page.exposeFunction('reportMessage', (name: string) => messages.push(name)) + await page.addInitScript(() => { + // pretend the page was opened from the settings, the bundle only posts a message then + Object.defineProperty(window, 'opener', { + value: { + postMessage: (data: { username: string }) => (window as unknown as { reportMessage: (name: string) => void }).reportMessage(data.username), + }, + }) + // the bundle closes the popup right after, which would end the test + window.close = () => {} + }) + + await page.goto('apps/integration_onedrive/popup-success?username=jane.doe%40example.com') + + await expect.poll(() => messages).toEqual(['jane.doe@example.com']) + }) +})