Updates
This commit is contained in:
7
nextcloud-aio/php/tests/.gitignore
vendored
Normal file
7
nextcloud-aio/php/tests/.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
# Playwright
|
||||
node_modules/
|
||||
/test-results/
|
||||
/playwright-report/
|
||||
/blob-report/
|
||||
/playwright/.cache/
|
||||
97
nextcloud-aio/php/tests/package-lock.json
generated
Normal file
97
nextcloud-aio/php/tests/package-lock.json
generated
Normal file
@@ -0,0 +1,97 @@
|
||||
{
|
||||
"name": "e2e",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "e2e",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.51.1",
|
||||
"@types/node": "^22.13.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.51.1",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.51.1.tgz",
|
||||
"integrity": "sha512-nM+kEaTSAoVlXmMPH10017vn3FSiFqr/bh4fKg9vmAdMfd9SDqRZNvPSiAHADc/itWak+qPvMPZQOPwCBW7k7Q==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.51.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.13.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.10.tgz",
|
||||
"integrity": "sha512-I6LPUvlRH+O6VRUqYOcMudhaIdUVWfsjnZavnsraHvpBwaEyMN29ry+0UVJhImYL16xsscu0aske3yA+uPOWfw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.51.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.51.1.tgz",
|
||||
"integrity": "sha512-kkx+MB2KQRkyxjYPc3a0wLZZoDczmppyGJIvQ43l+aZihkaVvmu/21kiyaHeHjiFxjxNNFnUncKmcGIyOojsaw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.51.1"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.51.1",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.1.tgz",
|
||||
"integrity": "sha512-/crRMj8+j/Nq5s8QcvegseuyeZPxpQCZb6HNk3Sos3BlZyAknRjoyJPFWkpNn8v0+P3WiwqFF8P+zQo4eqiNuw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.20.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
|
||||
"integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
8
nextcloud-aio/php/tests/package.json
Normal file
8
nextcloud-aio/php/tests/package.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"name": "nextcloud-aio-mastercontainer-tests",
|
||||
"version": "1.0.0",
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.51.1"
|
||||
}
|
||||
}
|
||||
29
nextcloud-aio/php/tests/playwright.config.js
Normal file
29
nextcloud-aio/php/tests/playwright.config.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import { defineConfig, devices } from '@playwright/test'
|
||||
|
||||
/**
|
||||
* @see https://playwright.dev/docs/test-configuration
|
||||
*/
|
||||
export default defineConfig({
|
||||
testDir: './tests',
|
||||
fullyParallel: false,
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: 0,
|
||||
workers: 1,
|
||||
reporter: [
|
||||
['list'],
|
||||
['html'],
|
||||
],
|
||||
use: {
|
||||
baseURL: process.env.BASE_URL ?? 'http://localhost:8080',
|
||||
trace: 'on',
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: {
|
||||
...devices['Desktop Chrome'],
|
||||
ignoreHTTPSErrors: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
96
nextcloud-aio/php/tests/tests/initial-setup.spec.js
Normal file
96
nextcloud-aio/php/tests/tests/initial-setup.spec.js
Normal file
@@ -0,0 +1,96 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { writeFileSync } from 'node:fs'
|
||||
|
||||
test('Initial setup', async ({ page: setupPage }) => {
|
||||
test.setTimeout(10 * 60 * 1000)
|
||||
|
||||
// Extract initial password
|
||||
await setupPage.goto('./setup');
|
||||
const password = await setupPage.locator('#initial-password').innerText()
|
||||
const containersPagePromise = setupPage.waitForEvent('popup');
|
||||
await setupPage.getByRole('link', { name: 'Open Nextcloud AIO login ↗' }).click();
|
||||
const containersPage = await containersPagePromise;
|
||||
|
||||
// Log in and wait for redirect
|
||||
await containersPage.locator('#master-password').click();
|
||||
await containersPage.locator('#master-password').fill(password);
|
||||
await containersPage.getByRole('button', { name: 'Log in' }).click();
|
||||
await containersPage.waitForURL('./containers');
|
||||
|
||||
// Reject IP addresses
|
||||
await containersPage.locator('#domain').click();
|
||||
await containersPage.locator('#domain').fill('1.1.1.1');
|
||||
await containersPage.getByRole('button', { name: 'Submit domain' }).click();
|
||||
await expect(containersPage.locator('body')).toContainText('Please enter a domain and not an IP-address!');
|
||||
|
||||
// Accept example.com (requires disabled domain validation)
|
||||
await containersPage.locator('#domain').click();
|
||||
await containersPage.locator('#domain').fill('example.com');
|
||||
await containersPage.getByRole('button', { name: 'Submit domain' }).click();
|
||||
|
||||
// Disable all additional containers
|
||||
await containersPage.locator('#talk').uncheck();
|
||||
await containersPage.getByRole('checkbox', { name: 'Whiteboard' }).uncheck();
|
||||
await containersPage.getByRole('checkbox', { name: 'Imaginary' }).uncheck();
|
||||
await containersPage.getByRole('checkbox', { name: 'Collabora' }).uncheck();
|
||||
await containersPage.getByRole('button', { name: 'Save changes' }).click();
|
||||
await expect(containersPage.locator('#talk')).not.toBeChecked()
|
||||
await expect(containersPage.getByRole('checkbox', { name: 'Whiteboard' })).not.toBeChecked()
|
||||
await expect(containersPage.getByRole('checkbox', { name: 'Imaginary' })).not.toBeChecked()
|
||||
await expect(containersPage.getByRole('checkbox', { name: 'Collabora' })).not.toBeChecked()
|
||||
|
||||
// Reject invalid time zones
|
||||
await containersPage.locator('#timezone').click();
|
||||
await containersPage.locator('#timezone').fill('Invalid time zone');
|
||||
containersPage.once('dialog', dialog => {
|
||||
console.log(`Dialog message: ${dialog.message()}`)
|
||||
dialog.accept()
|
||||
});
|
||||
await containersPage.getByRole('button', { name: 'Submit timezone' }).click();
|
||||
await expect(containersPage.locator('body')).toContainText('The entered timezone does not seem to be a valid timezone!')
|
||||
|
||||
// Accept valid time zone
|
||||
await containersPage.locator('#timezone').click();
|
||||
await containersPage.locator('#timezone').fill('Europe/Berlin');
|
||||
containersPage.once('dialog', dialog => {
|
||||
console.log(`Dialog message: ${dialog.message()}`)
|
||||
dialog.accept()
|
||||
});
|
||||
await containersPage.getByRole('button', { name: 'Submit timezone' }).click();
|
||||
|
||||
// Start containers and wait for starting message
|
||||
await containersPage.getByRole('button', { name: 'Download and start containers' }).click();
|
||||
await expect(containersPage.getByRole('main')).toContainText('Containers are currently starting.', { timeout: 5 * 60 * 1000 });
|
||||
await expect(containersPage.getByRole('link', { name: 'Open your Nextcloud ↗' })).toBeVisible({ timeout: 3 * 60 * 1000 });
|
||||
await expect(containersPage.getByRole('link', { name: 'Open your Nextcloud ↗' })).toHaveAttribute('href', 'https://example.com');
|
||||
|
||||
// Extract initial nextcloud password
|
||||
await expect(containersPage.getByRole('main')).toContainText('Initial Nextcloud password:')
|
||||
const initialNextcloudPassword = await containersPage.locator('#initial-nextcloud-password').innerText();
|
||||
|
||||
// Set backup location and create backup
|
||||
const borgBackupLocation = `/mnt/test/aio-${Math.floor(Math.random() * 2147483647)}`
|
||||
await containersPage.locator('#borg_backup_host_location').click();
|
||||
await containersPage.locator('#borg_backup_host_location').fill(borgBackupLocation);
|
||||
await containersPage.getByRole('button', { name: 'Submit backup location' }).click();
|
||||
containersPage.once('dialog', dialog => {
|
||||
console.log(`Dialog message: ${dialog.message()}`)
|
||||
dialog.accept()
|
||||
});
|
||||
await containersPage.getByRole('button', { name: 'Create backup' }).click();
|
||||
await expect(containersPage.getByRole('main')).toContainText('Backup container is currently running:', { timeout: 3 * 60 * 1000 });
|
||||
await expect(containersPage.getByRole('main')).toContainText('Last backup successful on', { timeout: 3 * 60 * 1000 });
|
||||
await containersPage.getByText('Click here to reveal all backup options').click();
|
||||
await expect(containersPage.locator('#borg-backup-password')).toBeVisible();
|
||||
const borgBackupPassword = await containersPage.locator('#borg-backup-password').innerText();
|
||||
|
||||
// Assert that all containers are stopped
|
||||
await expect(containersPage.getByRole('button', { name: 'Start containers' })).toBeVisible();
|
||||
|
||||
// Save passwords for restore backup test
|
||||
writeFileSync('test_data.json', JSON.stringify({
|
||||
initialNextcloudPassword,
|
||||
borgBackupLocation,
|
||||
borgBackupPassword,
|
||||
}))
|
||||
});
|
||||
83
nextcloud-aio/php/tests/tests/restore-instance.spec.js
Normal file
83
nextcloud-aio/php/tests/tests/restore-instance.spec.js
Normal file
@@ -0,0 +1,83 @@
|
||||
import { test, expect } from '@playwright/test';
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
test('Restore instance', async ({ page: setupPage }) => {
|
||||
test.setTimeout(10 * 60 * 1000)
|
||||
|
||||
// Load passwords from previous test
|
||||
const {
|
||||
initialNextcloudPassword,
|
||||
borgBackupLocation,
|
||||
borgBackupPassword,
|
||||
} = JSON.parse(readFileSync('test_data.json'))
|
||||
|
||||
// Extract initial password
|
||||
await setupPage.goto('./setup');
|
||||
const password = await setupPage.locator('#initial-password').innerText()
|
||||
const containersPagePromise = setupPage.waitForEvent('popup');
|
||||
await setupPage.getByRole('link', { name: 'Open Nextcloud AIO login ↗' }).click();
|
||||
const containersPage = await containersPagePromise;
|
||||
|
||||
// Log in and wait for redirect
|
||||
await containersPage.locator('#master-password').click();
|
||||
await containersPage.locator('#master-password').fill(password);
|
||||
await containersPage.getByRole('button', { name: 'Log in' }).click();
|
||||
await containersPage.waitForURL('./containers');
|
||||
|
||||
// Reject example.com (requires enabled domain validation)
|
||||
await containersPage.locator('#domain').click();
|
||||
await containersPage.locator('#domain').fill('example.com');
|
||||
await containersPage.getByRole('button', { name: 'Submit domain' }).click();
|
||||
await expect(containersPage.locator('body')).toContainText('Domain does not point to this server or the reverse proxy is not configured correctly.', { timeout: 15 * 1000 });
|
||||
|
||||
// Reject invalid backup location
|
||||
await containersPage.locator('#borg_restore_host_location').click();
|
||||
await containersPage.locator('#borg_restore_host_location').fill('/mnt/test/aio-incorrect-path');
|
||||
await containersPage.locator('#borg_restore_password').click();
|
||||
await containersPage.locator('#borg_restore_password').fill(borgBackupPassword);
|
||||
await containersPage.getByRole('button', { name: 'Submit location and encryption password' }).click()
|
||||
await containersPage.getByRole('button', { name: 'Test path and encryption' }).click();
|
||||
await expect(containersPage.getByRole('main')).toContainText('Last test failed!', { timeout: 60 * 1000 });
|
||||
|
||||
// Reject invalid backup password
|
||||
await containersPage.locator('#borg_restore_host_location').click();
|
||||
await containersPage.locator('#borg_restore_host_location').fill(borgBackupLocation);
|
||||
await containersPage.locator('#borg_restore_password').click();
|
||||
await containersPage.locator('#borg_restore_password').fill('foobar');
|
||||
await containersPage.getByRole('button', { name: 'Submit location and encryption password' }).click()
|
||||
await containersPage.getByRole('button', { name: 'Test path and encryption' }).click();
|
||||
await expect(containersPage.getByRole('main')).toContainText('Last test failed!', { timeout: 60 * 1000 });
|
||||
|
||||
// Accept correct backup location and password
|
||||
await containersPage.locator('#borg_restore_host_location').click();
|
||||
await containersPage.locator('#borg_restore_host_location').fill(borgBackupLocation);
|
||||
await containersPage.locator('#borg_restore_password').click();
|
||||
await containersPage.locator('#borg_restore_password').fill(borgBackupPassword);
|
||||
await containersPage.getByRole('button', { name: 'Submit location and encryption password' }).click()
|
||||
await containersPage.getByRole('button', { name: 'Test path and encryption' }).click();
|
||||
|
||||
// Check integrity and restore backup
|
||||
await containersPage.getByRole('button', { name: 'Check backup integrity' }).click();
|
||||
await expect(containersPage.getByRole('main')).toContainText('Last check successful!', { timeout: 5 * 60 * 1000 });
|
||||
containersPage.once('dialog', dialog => {
|
||||
console.log(`Dialog message: ${dialog.message()}`)
|
||||
dialog.accept()
|
||||
});
|
||||
await containersPage.getByRole('button', { name: 'Restore selected backup' }).click();
|
||||
await expect(containersPage.getByRole('main')).toContainText('Backup container is currently running:', { timeout: 1 * 60 * 1000 });
|
||||
|
||||
// Verify a successful backup restore
|
||||
await expect(containersPage.getByRole('main')).toContainText('Last restore successful!', { timeout: 3 * 60 * 1000 });
|
||||
await expect(containersPage.getByRole('main')).toContainText('⚠️ Container updates are available. Click on Stop containers and Start and update containers to update them. You should consider creating a backup first.');
|
||||
containersPage.once('dialog', dialog => {
|
||||
console.log(`Dialog message: ${dialog.message()}`)
|
||||
dialog.accept()
|
||||
});
|
||||
await containersPage.getByRole('button', { name: 'Start and update containers' }).click();
|
||||
await expect(containersPage.getByRole('link', { name: 'Open your Nextcloud ↗' })).toBeVisible({ timeout: 5 * 60 * 1000 });
|
||||
await expect(containersPage.getByRole('main')).toContainText(initialNextcloudPassword);
|
||||
|
||||
// Verify that containers are all stopped
|
||||
await containersPage.getByRole('button', { name: 'Stop containers' }).click();
|
||||
await expect(containersPage.getByRole('button', { name: 'Start containers' })).toBeVisible({ timeout: 60 * 1000 });
|
||||
});
|
||||
Reference in New Issue
Block a user