WIP tests

This commit is contained in:
Tanner Sommers 2024-09-17 12:19:12 -05:00
parent b04c204492
commit 9106b4f511
6 changed files with 222 additions and 8 deletions

View File

@ -119,6 +119,9 @@ importers:
'@fontsource/redaction-10':
specifier: ^5.0.2
version: 5.0.2
'@playwright/test':
specifier: ^1.47.1
version: 1.47.1
'@sveltejs/adapter-static':
specifier: ^3.0.2
version: 3.0.2(@sveltejs/kit@2.5.19(@sveltejs/vite-plugin-svelte@3.1.1(svelte@4.2.18)(vite@5.3.5(@types/node@20.14.14)))(svelte@4.2.18)(vite@5.3.5(@types/node@20.14.14)))
@ -135,7 +138,7 @@ importers:
specifier: ^2.1.25
version: 2.1.25
'@types/node':
specifier: ^20.14.10
specifier: ^20.14.14
version: 20.14.14
compare-versions:
specifier: ^6.1.0
@ -561,6 +564,11 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
'@playwright/test@1.47.1':
resolution: {integrity: sha512-dbWpcNQZ5nj16m+A5UNScYx7HX5trIy7g4phrcitn+Nk83S32EBX/CLU4hiF4RGKX/yRc93AAqtfaXB7JWBd4Q==}
engines: {node: '>=18'}
hasBin: true
'@polka/url@1.0.0-next.25':
resolution: {integrity: sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==}
@ -1320,6 +1328,11 @@ packages:
fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
fsevents@2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
os: [darwin]
fsevents@2.3.3:
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@ -1749,6 +1762,16 @@ packages:
resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
engines: {node: '>= 6'}
playwright-core@1.47.1:
resolution: {integrity: sha512-i1iyJdLftqtt51mEk6AhYFaAJCDx0xQ/O5NU8EKaWFgMjItPVma542Nh/Aq8aLCjIJSzjaiEQGW/nyqLkGF1OQ==}
engines: {node: '>=18'}
hasBin: true
playwright@1.47.1:
resolution: {integrity: sha512-SUEKi6947IqYbKxRiqnbUobVZY4bF1uu+ZnZNJX9DfU1tlf2UhWfvVjLf01pQx9URsOr18bFVUKXmanYWhbfkw==}
engines: {node: '>=18'}
hasBin: true
postcss-load-config@6.0.1:
resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}
engines: {node: '>= 18'}
@ -2525,6 +2548,10 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
'@playwright/test@1.47.1':
dependencies:
playwright: 1.47.1
'@polka/url@1.0.0-next.25': {}
'@rollup/rollup-android-arm-eabi@4.19.2':
@ -3342,6 +3369,9 @@ snapshots:
fs.realpath@1.0.0: {}
fsevents@2.3.2:
optional: true
fsevents@2.3.3:
optional: true
@ -3709,6 +3739,14 @@ snapshots:
pirates@4.0.6: {}
playwright-core@1.47.1: {}
playwright@1.47.1:
dependencies:
playwright-core: 1.47.1
optionalDependencies:
fsevents: 2.3.2
postcss-load-config@6.0.1(postcss@8.4.40):
dependencies:
lilconfig: 3.1.2

5
web/.gitignore vendored
View File

@ -6,3 +6,8 @@
# vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
node_modules/
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/

View File

@ -8,7 +8,10 @@
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"test": "playwright test",
"test:headfull": "playwright test --headed",
"test:withui": "playwright test --ui"
},
"license": "CC-BY-NC-SA-4.0",
"engines": {
@ -26,12 +29,13 @@
"devDependencies": {
"@eslint/js": "^9.5.0",
"@fontsource/redaction-10": "^5.0.2",
"@playwright/test": "^1.47.1",
"@sveltejs/adapter-static": "^3.0.2",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@types/eslint__js": "^8.42.3",
"@types/fluent-ffmpeg": "^2.1.25",
"@types/node": "^20.14.10",
"@types/node": "^20.14.14",
"compare-versions": "^6.1.0",
"eslint": "^8.57.0",
"glob": "^10.4.5",

44
web/playwright.config.ts Normal file
View File

@ -0,0 +1,44 @@
import { defineConfig, devices } from '@playwright/test';
// See https://playwright.dev/docs/test-configuration.
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
trace: 'on-first-retry',
baseURL: 'https://localhost:5173',
ignoreHTTPSErrors: true, // Cobalt uses a self-signed certificate in development
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
// {
// name: 'firefox',
// use: { ...devices['Desktop Firefox'] },
// },
// {
// name: 'webkit',
// use: { ...devices['Desktop Safari'] },
// },
// Test on mobile devices (might be useful in the future)?
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },
],
});

View File

@ -1,13 +1,16 @@
import { get } from "svelte/store";
import { get } from 'svelte/store';
import env, { apiURL } from "$lib/env";
import settings from "$lib/state/settings";
import env, { apiURL } from '$lib/env';
import settings from '$lib/state/settings';
export const currentApiURL = () => {
const processingSettings = get(settings).processing;
const customInstanceURL = processingSettings.customInstanceURL;
if (processingSettings.enableCustomInstances && customInstanceURL.length > 0) {
if (
processingSettings.enableCustomInstances &&
customInstanceURL.length > 0
) {
return new URL(customInstanceURL).origin;
}
@ -16,4 +19,4 @@ export const currentApiURL = () => {
}
return new URL(apiURL).origin;
}
};

120
web/tests/home.spec.ts Normal file
View File

@ -0,0 +1,120 @@
import { test, expect } from '@playwright/test';
const VIDEO_TEST_LINK = 'https://www.youtube.com/watch?v=dQw4w9WgXcQ'; // :3
// Before each test, open the page (/) in the browser and wait for the
// page to load.
test.beforeEach(async ({ page }) => {
await page.goto('/');
await page.waitForLoadState('networkidle', { timeout: 10000 });
});
// Test the branding of the page.
test('branding', async ({ page }) => {
// Page has a tile of "cobalt"
const title = await page.title();
expect(title).toBe('cobalt');
/*
* Check if the omnibar is present on the page and has correct branding.
* IDs: omnibox, meowbalt (branding), cobalt-save (root main tag)
*/
const requiredOmniBarElements = ['omnibox', 'cobalt-save'];
for (const element of requiredOmniBarElements) {
expect(await page.isVisible(`#${element}`)).toBe(true);
}
// Make sure meowbalt is in the correct default state (visible, class set to "meowbalt smile")
// note: meowbalt is a class, not an id
const meowbalt = await page.$('.meowbalt');
expect(await meowbalt?.isVisible()).toBe(true);
expect(await meowbalt?.getAttribute('class')).toContain('meowbalt smile');
expect(await meowbalt?.getAttribute('src')).toBe('/meowbalt/smile.png');
// Aria-hidden attribute is set to true, alt = "meowbalt"
expect(await meowbalt?.getAttribute('aria-hidden')).toBe('true');
expect(await meowbalt?.getAttribute('alt')).toBe('meowbalt');
});
// Omnibar related tests
test('omnibar', async ({ page }) => {
// Check if the omnibar is present on the page
expect(await page.isVisible('#omnibox')).toBe(true);
// Input field is present and has the correct placeholder
const input = await page.$('#omnibox input');
expect(await input?.isVisible()).toBe(true);
expect(await input?.getAttribute('placeholder')).toBe(
'paste the link here',
);
// Check that all buttons can be clicked #action-container (besides the first one and the last one)
const buttons = await (
await page.$$('#action-container button')
).slice(1, -1);
for (const button of buttons) {
await button.click();
// Check if the button has the "selected" class after clicking
expect(await button.getAttribute('class')).toContain('active');
// Make sure the aria-pressed attribute is set to true
expect(await button.getAttribute('aria-pressed')).toBe('true');
}
});
test('supported services', async ({ page }) => {
// Check if the supported services are present on the page
expect(await page.isVisible('#supported-services')).toBe(true);
// Click the services-button and expect the dropdown to be visible (services-popover)
// Visibility of the popover is when the popover has the "expanded" class
const servicesButton = await page.$('#services-button');
const servicesPopover = await page.$('#services-popover');
await servicesButton?.click();
// Check if the services popover is visible
expect(await servicesPopover?.getAttribute('class')).toContain('expanded');
await servicesButton?.click();
expect(await servicesPopover?.getAttribute('class')).not.toContain(
'expanded',
);
// Now open the services popover and ensure skeleton is present
await servicesButton?.click();
// Get all the skeleton elements
const skeletonElements = await page.$$('#services-popover .skeleton');
expect(skeletonElements.length).toBeGreaterThan(0);
// Now wait for the services to load and ensure the skeleton is gone (max of 10 seconds)
await page.waitForSelector('#services-popover .service-item', {
state: 'attached',
timeout: 10000,
});
});
test('download example', async ({ page }) => {
// Get the input field and set the value to the test link
const input = await page.$('#omnibox input');
await input?.fill(VIDEO_TEST_LINK);
// Click the download button
const downloadButton = await page.$('#download-button');
await downloadButton?.click();
// Wait for the network request to api.cobalt.tools to finish
const req = await page.waitForRequest('**/api.cobalt.tools/');
const rsp = await req.response();
expect(rsp).not.toBe(null);
// Ensure we have a 200 OK and a valid JSON response
expect(rsp!.status()).toBe(200);
const json = await rsp!.json();
// Json should contain the following keys: "status", "url", "and filename"
const props = ['status', 'url', 'filename'];
for (const prop of props) {
expect(json).toHaveProperty(prop);
}
});