More end-to-end tests
All checks were successful
Build and test project / build-and-test (push) Successful in 1m43s
All checks were successful
Build and test project / build-and-test (push) Successful in 1m43s
This commit is contained in:
@@ -4,6 +4,7 @@ const fs = require('fs');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
const zlib = require('zlib');
|
const zlib = require('zlib');
|
||||||
|
const JSZip = require('jszip');
|
||||||
|
|
||||||
// Expected SHA1 checksums for Kobo Libra Color, firmware 4.45.23646,
|
// Expected SHA1 checksums for Kobo Libra Color, firmware 4.45.23646,
|
||||||
// with only "Remove footer (row3) on new home screen" enabled.
|
// with only "Remove footer (row3) on new home screen" enabled.
|
||||||
@@ -296,6 +297,9 @@ test.describe('NickelMenu', () => {
|
|||||||
await expect(page.locator('input[name="nm-cfg-simplify-tabs"]')).not.toBeChecked();
|
await expect(page.locator('input[name="nm-cfg-simplify-tabs"]')).not.toBeChecked();
|
||||||
await expect(page.locator('input[name="nm-cfg-simplify-home"]')).not.toBeChecked();
|
await expect(page.locator('input[name="nm-cfg-simplify-home"]')).not.toBeChecked();
|
||||||
|
|
||||||
|
// Enable simplifyHome for testing
|
||||||
|
await page.check('input[name="nm-cfg-simplify-home"]');
|
||||||
|
|
||||||
await expect(page.locator('#btn-nm-next')).toBeEnabled();
|
await expect(page.locator('#btn-nm-next')).toBeEnabled();
|
||||||
await page.click('#btn-nm-next');
|
await page.click('#btn-nm-next');
|
||||||
|
|
||||||
@@ -310,13 +314,36 @@ test.describe('NickelMenu', () => {
|
|||||||
await expect(page.locator('#btn-nm-download')).toBeVisible();
|
await expect(page.locator('#btn-nm-download')).toBeVisible();
|
||||||
|
|
||||||
// Click download and wait for done step
|
// Click download and wait for done step
|
||||||
await page.click('#btn-nm-download');
|
const [download] = await Promise.all([
|
||||||
|
page.waitForEvent('download'),
|
||||||
|
page.click('#btn-nm-download'),
|
||||||
|
]);
|
||||||
await expect(page.locator('#step-nm-done')).toBeVisible({ timeout: 30_000 });
|
await expect(page.locator('#step-nm-done')).toBeVisible({ timeout: 30_000 });
|
||||||
await expect(page.locator('#nm-done-status')).toContainText('ready to download');
|
await expect(page.locator('#nm-done-status')).toContainText('ready to download');
|
||||||
|
|
||||||
// Download instructions should be visible, and include eReader.conf step for sample config
|
// Download instructions should be visible, and include eReader.conf step for sample config
|
||||||
await expect(page.locator('#nm-download-instructions')).not.toBeHidden();
|
await expect(page.locator('#nm-download-instructions')).not.toBeHidden();
|
||||||
await expect(page.locator('#nm-download-conf-step')).not.toBeHidden();
|
await expect(page.locator('#nm-download-conf-step')).not.toBeHidden();
|
||||||
|
|
||||||
|
// Verify ZIP contents
|
||||||
|
expect(download.suggestedFilename()).toBe('NickelMenu-install.zip');
|
||||||
|
const zipData = fs.readFileSync(await download.path());
|
||||||
|
const zip = await JSZip.loadAsync(zipData);
|
||||||
|
const zipFiles = Object.keys(zip.files);
|
||||||
|
|
||||||
|
// Must contain KoboRoot.tgz
|
||||||
|
expect(zipFiles).toContainEqual('.kobo/KoboRoot.tgz');
|
||||||
|
// Must contain NickelMenu items config
|
||||||
|
expect(zipFiles).toContainEqual('.adds/nm/items');
|
||||||
|
// Must contain font files (fonts checkbox is checked by default)
|
||||||
|
expect(zipFiles.some(f => f.startsWith('fonts/'))).toBe(true);
|
||||||
|
// Must NOT contain screensaver (unchecked by default)
|
||||||
|
expect(zipFiles.some(f => f.startsWith('.kobo/screensaver/'))).toBe(false);
|
||||||
|
|
||||||
|
// Verify items file has simplifyHome modifications
|
||||||
|
const itemsContent = await zip.file('.adds/nm/items').async('string');
|
||||||
|
expect(itemsContent).toContain('experimental:hide_home_row1col2_enabled:1');
|
||||||
|
expect(itemsContent).toContain('experimental:hide_home_row3_enabled:1');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('no device — install NickelMenu only via manual download', async ({ page }) => {
|
test('no device — install NickelMenu only via manual download', async ({ page }) => {
|
||||||
@@ -337,12 +364,23 @@ test.describe('NickelMenu', () => {
|
|||||||
await expect(page.locator('#nm-review-list')).toContainText('NickelMenu (KoboRoot.tgz)');
|
await expect(page.locator('#nm-review-list')).toContainText('NickelMenu (KoboRoot.tgz)');
|
||||||
|
|
||||||
// Download
|
// Download
|
||||||
await page.click('#btn-nm-download');
|
const [download] = await Promise.all([
|
||||||
|
page.waitForEvent('download'),
|
||||||
|
page.click('#btn-nm-download'),
|
||||||
|
]);
|
||||||
await expect(page.locator('#step-nm-done')).toBeVisible({ timeout: 30_000 });
|
await expect(page.locator('#step-nm-done')).toBeVisible({ timeout: 30_000 });
|
||||||
await expect(page.locator('#nm-done-status')).toContainText('ready to download');
|
await expect(page.locator('#nm-done-status')).toContainText('ready to download');
|
||||||
|
|
||||||
// eReader.conf step should be hidden for nickelmenu-only
|
// eReader.conf step should be hidden for nickelmenu-only
|
||||||
await expect(page.locator('#nm-download-conf-step')).toBeHidden();
|
await expect(page.locator('#nm-download-conf-step')).toBeHidden();
|
||||||
|
|
||||||
|
// Verify ZIP contents — should only contain KoboRoot.tgz
|
||||||
|
expect(download.suggestedFilename()).toBe('NickelMenu-install.zip');
|
||||||
|
const zipData = fs.readFileSync(await download.path());
|
||||||
|
const zip = await JSZip.loadAsync(zipData);
|
||||||
|
const zipFiles = Object.keys(zip.files).filter(f => !zip.files[f].dir);
|
||||||
|
|
||||||
|
expect(zipFiles).toEqual(['.kobo/KoboRoot.tgz']);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('no device — remove option is disabled in manual mode', async ({ page }) => {
|
test('no device — remove option is disabled in manual mode', async ({ page }) => {
|
||||||
@@ -421,6 +459,40 @@ test.describe('NickelMenu', () => {
|
|||||||
expect(items).toContain('experimental:hide_home_row3_enabled:1');
|
expect(items).toContain('experimental:hide_home_row3_enabled:1');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('with device — install NickelMenu only and write to Kobo', async ({ page }) => {
|
||||||
|
test.skip(!hasNickelMenuAssets(), 'NickelMenu assets not found in webroot');
|
||||||
|
|
||||||
|
await connectMockDevice(page, { hasNickelMenu: false });
|
||||||
|
|
||||||
|
// Continue to mode selection
|
||||||
|
await page.click('#btn-device-next');
|
||||||
|
await page.click('#btn-mode-next');
|
||||||
|
|
||||||
|
// NickelMenu configure step
|
||||||
|
await expect(page.locator('#step-nickelmenu')).not.toBeHidden();
|
||||||
|
|
||||||
|
// Select "Install NickelMenu only"
|
||||||
|
await page.click('input[name="nm-option"][value="nickelmenu-only"]');
|
||||||
|
await expect(page.locator('#nm-config-options')).toBeHidden();
|
||||||
|
|
||||||
|
await page.click('#btn-nm-next');
|
||||||
|
|
||||||
|
// Review step
|
||||||
|
await expect(page.locator('#step-nm-review')).not.toBeHidden();
|
||||||
|
await expect(page.locator('#nm-review-list')).toContainText('NickelMenu (KoboRoot.tgz)');
|
||||||
|
|
||||||
|
// Write to device
|
||||||
|
await page.click('#btn-nm-write');
|
||||||
|
await expect(page.locator('#step-nm-done')).toBeVisible({ timeout: 30_000 });
|
||||||
|
await expect(page.locator('#nm-done-status')).toContainText('installed');
|
||||||
|
|
||||||
|
// Verify only KoboRoot.tgz was written (no config files)
|
||||||
|
const writtenFiles = await getWrittenFiles(page);
|
||||||
|
expect(writtenFiles).toContainEqual(expect.stringContaining('KoboRoot.tgz'));
|
||||||
|
// Should NOT have written items, fonts, etc.
|
||||||
|
expect(writtenFiles.filter(f => !f.includes('KoboRoot.tgz'))).toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
test('with device — remove NickelMenu', async ({ page }) => {
|
test('with device — remove NickelMenu', async ({ page }) => {
|
||||||
test.skip(!hasNickelMenuAssets(), 'NickelMenu assets not found in webroot');
|
test.skip(!hasNickelMenuAssets(), 'NickelMenu assets not found in webroot');
|
||||||
|
|
||||||
|
|||||||
115
tests/e2e/package-lock.json
generated
115
tests/e2e/package-lock.json
generated
@@ -6,7 +6,8 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "kobopatch-webui-e2e",
|
"name": "kobopatch-webui-e2e",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.50.0"
|
"@playwright/test": "^1.50.0",
|
||||||
|
"jszip": "^3.10.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@playwright/test": {
|
"node_modules/@playwright/test": {
|
||||||
@@ -25,6 +26,13 @@
|
|||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/core-util-is": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
@@ -40,6 +48,57 @@
|
|||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/immediate": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
|
"node_modules/isarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/jszip": {
|
||||||
|
"version": "3.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
|
||||||
|
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "(MIT OR GPL-3.0-or-later)",
|
||||||
|
"dependencies": {
|
||||||
|
"lie": "~3.3.0",
|
||||||
|
"pako": "~1.0.2",
|
||||||
|
"readable-stream": "~2.3.6",
|
||||||
|
"setimmediate": "^1.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lie": {
|
||||||
|
"version": "3.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||||
|
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"immediate": "~3.0.5"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pako": {
|
||||||
|
"version": "1.0.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||||
|
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "(MIT AND Zlib)"
|
||||||
|
},
|
||||||
"node_modules/playwright": {
|
"node_modules/playwright": {
|
||||||
"version": "1.58.2",
|
"version": "1.58.2",
|
||||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz",
|
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz",
|
||||||
@@ -71,6 +130,60 @@
|
|||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"node_modules/process-nextick-args": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/readable-stream": {
|
||||||
|
"version": "2.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||||
|
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.3",
|
||||||
|
"isarray": "~1.0.0",
|
||||||
|
"process-nextick-args": "~2.0.0",
|
||||||
|
"safe-buffer": "~5.1.1",
|
||||||
|
"string_decoder": "~1.1.1",
|
||||||
|
"util-deprecate": "~1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/setimmediate": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/string_decoder": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
"test": "npx playwright test"
|
"test": "npx playwright test"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.50.0"
|
"@playwright/test": "^1.50.0",
|
||||||
|
"jszip": "^3.10.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user