Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
# SPDX-License-Identifier: MIT

name: Playwright Tests
on:
pull_request:
branches: [main]

jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
steps:
- name: Checkout app
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

- name: Check composer.json
id: check_composer
uses: andstor/file-existence-action@076e0072799f4942c8bc574a82233e1e4d13e9d6 # v2
with:
files: 'composer.json'

- name: Install composer dependencies
if: steps.check_composer.outputs.files_exists == 'true'
run: composer install --no-dev

- name: Read package.json node and npm engines version
uses: skjnldsv/read-package-engines-version-actions@06d6baf7d8f41934ab630e97d9e6c0bc9c9ac5e4 # v3
id: versions
with:
fallbackNode: '^20'
fallbackNpm: '^10'

- name: Set up node ${{ steps.versions.outputs.nodeVersion }}
uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
with:
node-version: ${{ steps.versions.outputs.nodeVersion }}

- name: Set up npm ${{ steps.versions.outputs.npmVersion }}
run: npm i -g npm@"${{ steps.versions.outputs.npmVersion }}"

- name: Install node dependencies & build app
run: |
npm ci
TESTING=true npm run build --if-present

- name: Install Playwright Browsers
run: npx playwright install chromium --only-shell

- name: Run Playwright tests
run: npx playwright test

- uses: actions/upload-artifact@v5
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ cypress/snapshots/actual
cypress/snapshots/diff
cypress/videos/
cypress/downloads/
/playwright-report/
.php-cs-fixer.cache
/test-results/
/tests/clover.xml
/tests/.phpunit.result.cache
dist/
Expand Down
98 changes: 98 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
"scripts": {
"build": "NODE_ENV=production NODE_OPTIONS='--max-old-space-size=4096' vite --mode production build",
"dev": "NODE_ENV=development NODE_OPTIONS='--max-old-space-size=4096' vite --mode development build",
"lint": "tsc && ESLINT_USE_FLAT_CONFIG=false eslint --ext .js,.ts,.vue src cypress",
"lint:fix": "tsc && ESLINT_USE_FLAT_CONFIG=false eslint --ext .js,.ts,.vue src cypress --fix",
"lint": "tsc && ESLINT_USE_FLAT_CONFIG=false eslint --ext .js,.ts,.vue src cypress playwright",
"lint:fix": "tsc && ESLINT_USE_FLAT_CONFIG=false eslint --ext .js,.ts,.vue src cypress playwright --fix",
"prettier": "prettier --check .",
"prettier:change": "git diff HEAD --name-only | xargs prettier --write --no-error-on-unmatched-pattern",
"prettier:fix": "prettier --write .",
"serve": "BASE=${BASE:-/apps/text} NODE_ENV=development vite --mode development serve --host",
"start:nextcloud": "node playwright/start-nextcloud-server.mjs",
"test": "NODE_ENV=test vitest run",
"test:coverage": "NODE_ENV=test vitest run --coverage",
"test:cypress": "cd cypress && ./runLocal.sh run",
Expand Down Expand Up @@ -116,6 +117,7 @@
"@nextcloud/eslint-config": "^8.4.2",
"@nextcloud/prettier-config": "^1.2.0",
"@nextcloud/vite-config": "^1.7.2",
"@playwright/test": "^1.56.1",
"@types/markdown-it": "^14.1.2",
"@vitejs/plugin-vue2": "^2.3.4",
"@vitest/coverage-v8": "^4.0.3",
Expand Down
58 changes: 58 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { defineConfig, devices } from '@playwright/test'

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './playwright',

/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: process.env.CI ? 'github' : 'list',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('./')`. */
baseURL: process.env.baseURL ?? 'http://localhost:8089/index.php/',

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},

projects: [
// Our global setup to configure the Nextcloud docker container
{
name: 'setup',
testMatch: /setup\.ts$/,
},

{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
},
dependencies: ['setup'],
},
],

webServer: {
// Starts the Nextcloud docker container
command: 'npm run start:nextcloud',
reuseExistingServer: !process.env.CI,
url: 'http://127.0.0.1:8089',
stderr: 'pipe',
stdout: 'pipe',
timeout: 5 * 60 * 1000, // max. 5 minutes for creating the container
},
})
74 changes: 74 additions & 0 deletions playwright/e2e/offline.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/**
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { type CDPSession, expect, mergeTests } from '@playwright/test'
import { test as randomUserTest } from '../support/fixtures/random-user'
import { test as uploadFileTest } from '../support/fixtures/upload-file'

const test = mergeTests(randomUserTest, uploadFileTest)

const setOnline = async (client: CDPSession, online: boolean): Promise<void> => {
if (online) {
await client.send('Network.emulateNetworkConditions', {
offline: false,
latency: 0,
downloadThroughput: -1,
uploadThroughput: -1,
})
await client.send('Network.disable')
} else {
await client.send('Network.enable')
await client.send('Network.emulateNetworkConditions', {
offline: true,
latency: 0,
downloadThroughput: 0,
uploadThroughput: 0,
})
}
}

test.beforeEach(async ({ page, file }) => {
await page.goto(`f/${file.fileId}`)
})

test.describe('Offline', () => {
test('Offline state indicator', async ({ context, page }) => {
await expect(page.locator('.session-list')).toBeVisible()
await expect(page.locator('.offline-state')).not.toBeVisible()

const client = await context.newCDPSession(page)
await setOnline(client, false)

await expect(page.locator('.session-list')).not.toBeVisible()
await expect(page.locator('.offline-state')).toBeVisible()

await setOnline(client, true)
})

test('Disabled upload and link file when offline', async ({ context, page }) => {
await page.locator('[data-text-action-entry="insert-link"]').click()
await expect(
page.locator('[data-text-action-entry="insert-link-file"] button'),
).toBeEnabled()
await page.locator('[data-text-action-entry="insert-link"]').click()
await expect(
page.locator('[data-text-action-entry="insert-attachment"] button'),
).toBeEnabled()

const client = await context.newCDPSession(page)
await setOnline(client, false)

await page.locator('[data-text-action-entry="insert-link"]').click()
await expect(
page.locator('[data-text-action-entry="insert-link-file"] button'),
).toBeDisabled()
await page.locator('[data-text-action-entry="insert-link"]').click()
await expect(
page.locator('[data-text-action-entry="insert-attachment"] button'),
).toBeDisabled()

await setOnline(client, true)
})
})
39 changes: 39 additions & 0 deletions playwright/start-nextcloud-server.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* SPDX-FileCopyrightText: 2024 Ferdinand Thiessen <[email protected]>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import { startNextcloud, stopNextcloud } from '@nextcloud/e2e-test-server/docker'
import { readFileSync } from 'fs'

const start = async () => {
return await startNextcloud(getBranch(), true, {
exposePort: 8089,
})
}

const getBranch = () => {
try {
const appinfo = readFileSync('appinfo/info.xml').toString()
const maxVersion = appinfo.match(
/<nextcloud min-version="\d+" max-version="(\d\d+)" \/>/,
)?.[1]
return maxVersion ? `stable${maxVersion}` : undefined
} catch (err) {
if (err.code === 'ENOENT') {
console.warn('No appinfo/info.xml found. Using default server banch.')
}
}
}

// Start the Nextcloud docker container
await start()
// Listen for process to exit (tests done) and shut down the docker container
process.on('beforeExit', (code) => {
stopNextcloud()
})

// Idle to wait for shutdown
while (true) {
await new Promise((resolve) => setTimeout(resolve, 5000))
}
Loading
Loading