From 029b55e17968957e2c311d40381d47036459ef3f Mon Sep 17 00:00:00 2001 From: Max Milton Date: Sat, 14 Dec 2024 13:07:08 +0900 Subject: [PATCH 01/15] chore: Tweak package.json meta data --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index eacc820f..8c8c4e87 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,8 @@ "name": "new-tab", "version": "0.23.0", "description": "⚡ A high-performance new tab page that gets you where you need to go faster.", - "repository": "maxmilton/new-tab", + "repository": "github:maxmilton/new-tab", + "bugs": "https://github.com/maxmilton/new-tab/issues", "homepage": "https://github.com/maxmilton/new-tab", "author": "Max Milton ", "license": "MIT", From c4a0d066e5ffd0441932b637e808ed8d60938b76 Mon Sep 17 00:00:00 2001 From: Max Milton Date: Sat, 14 Dec 2024 13:07:51 +0900 Subject: [PATCH 02/15] chore: Update dependencies --- bun.lockb | Bin 114316 -> 114212 bytes package.json | 28 ++++++++++++++-------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/bun.lockb b/bun.lockb index e26e5438b112a45a64c9d759f97947ce64fd6e88..30fb9bf1c710ff5a60db7a2cfbdd82c12e09f061 100755 GIT binary patch delta 27343 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 ZZ$~)#7R3xpol4m)#=J|7cd$IY{{y?uD}Vq1 delta 27269 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%0Dw9Wr)eKI>Wp|$ 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 Date: Sat, 14 Dec 2024 13:08:21 +0900 Subject: [PATCH 03/15] chore: CI yml formatting --- .github/workflows/ci.yml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/semgrep-analysis.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 29fba5ba..f00dec53 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,7 +2,7 @@ name: ci on: push: branches: [master, next] - paths-ignore: ['**.md'] + paths-ignore: ["**.md"] pull_request: {} workflow_dispatch: {} concurrency: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4b049852..d54ca6dc 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -5,7 +5,7 @@ on: pull_request: {} workflow_dispatch: {} schedule: - - cron: '28 6 * * 4' + - cron: "28 6 * * 4" concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.head.label || github.run_id }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} diff --git a/.github/workflows/semgrep-analysis.yml b/.github/workflows/semgrep-analysis.yml index fe659b69..1ae40099 100644 --- a/.github/workflows/semgrep-analysis.yml +++ b/.github/workflows/semgrep-analysis.yml @@ -5,7 +5,7 @@ on: pull_request: {} workflow_dispatch: {} schedule: - - cron: '28 6 * * 4' + - cron: "28 6 * * 4" concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.head.label || github.run_id }} cancel-in-progress: ${{ github.event_name == 'pull_request' }} From 77fb60b2125d9bbb92859e13099793ca97adfa16 Mon Sep 17 00:00:00 2001 From: Max Milton Date: Sat, 14 Dec 2024 13:08:49 +0900 Subject: [PATCH 04/15] feat: Minifest tweaks --- manifest.config.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/manifest.config.ts b/manifest.config.ts index 893ee078..4d42ccc6 100644 --- a/manifest.config.ts +++ b/manifest.config.ts @@ -17,6 +17,7 @@ function gitRef() { } // FIXME: Remove these once @types/chrome is updated. +// https://developer.chrome.com/docs/extensions/mv3/cross-origin-isolation/ interface ManifestExtra { /** https://developer.chrome.com/docs/extensions/mv3/manifest/cross_origin_embedder_policy/ */ cross_origin_embedder_policy?: { @@ -35,7 +36,7 @@ export const createManifest = ( name: 'New Tab', description: pkg.description, homepage_url: pkg.homepage, - version: pkg.version, + version: pkg.version.split('-')[0], // shippable releases should not have a named version version_name: debug ? gitRef() : undefined, minimum_chrome_version: '123', // for light-dark() CSS function @@ -68,10 +69,10 @@ export const createManifest = ( content_security_policy: { extension_pages: [ "default-src 'none'", + "base-uri 'none'", "script-src 'self'", "style-src 'self'", "img-src 'self'", - "base-uri 'none'", '', // include trailing semicolon ].join(';'), }, From 3bd4b63d1b543a7e658c050bab7f3971450cb477 Mon Sep 17 00:00:00 2001 From: Max Milton Date: Sat, 14 Dec 2024 13:09:01 +0900 Subject: [PATCH 05/15] chore: Tweak Biome config --- biome.jsonc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/biome.jsonc b/biome.jsonc index 118c702f..ebafabd5 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -117,6 +117,9 @@ }, "style": { "noNamespaceImport": "off" + }, + "nursery": { + "noDynamicNamespaceImportAccess": "off" } } }, From 1cd84801fe7365e714ba8d8169a343eeb90cc48b Mon Sep 17 00:00:00 2001 From: Max Milton Date: Sat, 14 Dec 2024 13:10:47 +0900 Subject: [PATCH 06/15] feat: Minor tweaks --- src/components/BookmarkBar.ts | 6 ++++++ src/utils.ts | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/BookmarkBar.ts b/src/components/BookmarkBar.ts index 0e474be6..a993f0eb 100644 --- a/src/components/BookmarkBar.ts +++ b/src/components/BookmarkBar.ts @@ -48,6 +48,7 @@ export const BookmarkBar = (): BookmarkBarComponent => { let index = 0; let node: ReturnType; + // Add one bookmark at a time until we overflow the max width for (; index < len; index++) { node = append(BookmarkNode(bookmarks[index]), root); width += node.clientWidth; @@ -131,3 +132,8 @@ export const BookmarkBar = (): BookmarkBarComponent => { return root; }; + +// // Improve performance of lookups on DOM nodes +// // @ts-expect-error -- add new properties to HTMLElement +// // eslint-disable-next-line no-multi-assign +// Element.prototype.__mouseover = Element.prototype.__mouseout = undefined; diff --git a/src/utils.ts b/src/utils.ts index 3869d9f2..b60aad2f 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,7 +1,7 @@ import type { UserStorageData } from './types'; performance.mark('Load Storage'); -export const storage: UserStorageData = await chrome.storage.local.get(); +export const storage = await chrome.storage.local.get(); // NOTE: When updating also update references that lookup items by index export const DEFAULT_SECTION_ORDER = [ @@ -17,7 +17,7 @@ declare const s: HTMLInputElement; // Simplified synthetic click event implementation of setupSyntheticEvent() from // stage1, plus special handling for browser internal links (e.g. chrome://) -// https://github.com/maxmilton/stage1/blob/master/src/events.ts +// @see https://github.com/maxmilton/stage1/blob/master/src/events.ts // eslint-disable-next-line @typescript-eslint/no-invalid-void-type, consistent-return export const handleClick = (event: MouseEvent): false | void => { let node = event.target as From 7198d41e13aa1006c584ac1c237d93e327b922aa Mon Sep 17 00:00:00 2001 From: Max Milton Date: Sat, 14 Dec 2024 13:11:11 +0900 Subject: [PATCH 07/15] feat: Byte savings + faster search --- src/components/SearchResult.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/SearchResult.ts b/src/components/SearchResult.ts index 41235ca5..ef599cc1 100644 --- a/src/components/SearchResult.ts +++ b/src/components/SearchResult.ts @@ -94,9 +94,7 @@ export const SearchResult = ( root.$$filter = (text) => renderList( rawData.filter((item) => - (item.title + '[' + item.url) - .toLowerCase() - .includes(text.toLowerCase()), + (item.title + item.url).toLowerCase().includes(text.toLowerCase()), ), ); From f03c277542894747ab109686619d997ef4e1ea6b Mon Sep 17 00:00:00 2001 From: Max Milton Date: Sat, 14 Dec 2024 13:12:30 +0900 Subject: [PATCH 08/15] chore: Refactor build --- build.ts | 108 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 64 insertions(+), 44 deletions(-) diff --git a/build.ts b/build.ts index 4fb63ed3..8bab4378 100644 --- a/build.ts +++ b/build.ts @@ -48,11 +48,9 @@ function compileCSS(src: string, from: string) { } /** - * Construct HTML and CSS files and save them to disk. + * Construct HTML file and save it to disk. */ -async function makeHTML(pageName: string, stylePath: string) { - const styleSrc = await Bun.file(stylePath).text(); - const css = compileCSS(styleSrc, stylePath); +async function makeHTML(pageName: string) { const template = ` @@ -64,10 +62,18 @@ async function makeHTML(pageName: string, stylePath: string) { .trim() .replaceAll(/\n\s+/g, '\n'); // remove leading whitespace - await Bun.write(`dist/${pageName}.css`, css); await Bun.write(`dist/${pageName}.html`, template); } +/** + * Construct CSS file and save it to disk. + */ +async function makeCSS(pageName: string, cssEntrypoint: string) { + const cssSource = await Bun.file(cssEntrypoint).text(); + const css = compileCSS(cssSource, cssEntrypoint); + await Bun.write(`dist/${pageName}.css`, css); +} + /** * Compile all themes, combine into a single JSON file, and save it to disk. */ @@ -86,29 +92,37 @@ async function makeThemes() { await Bun.write('dist/themes.json', JSON.stringify(themes)); } -async function minifyJS(artifact: BuildArtifact) { - let source = await artifact.text(); - - // Improve collapsing variables; terser doesn't do this so we do it manually. - source = source.replaceAll('const ', 'let '); - - const result = await terser.minify(source, { - ecma: 2020, - module: true, - compress: { - reduce_funcs: false, // prevent functions being inlined - // XXX: Comment out to keep performance markers for debugging. - pure_funcs: ['performance.mark', 'performance.measure'], - passes: 3, - }, - mangle: { - properties: { - regex: /^\$\$|^__click$/, - }, - }, - }); - - await Bun.write(artifact.path, result.code ?? ''); +async function minifyJS(artifacts: BuildArtifact[]) { + for (const artifact of artifacts) { + if (artifact.kind === 'entry-point' || artifact.kind === 'chunk') { + const source = await artifact.text(); + const result = await terser.minify(source, { + ecma: 2020, + module: true, + compress: { + comparisons: false, + negate_iife: false, + reduce_funcs: false, // prevent function inlining + passes: 3, + // XXX: Comment out to keep performance markers for debugging. + pure_funcs: ['performance.mark', 'performance.measure'], + }, + mangle: { + properties: { + regex: /^\$\$|^__click$/, + }, + }, + format: { + wrap_func_args: true, + wrap_iife: true, + }, + }); + + if (result.code) { + await Bun.write(artifact.path, result.code); + } + } + } } console.time('prebuild'); @@ -121,29 +135,35 @@ console.time('manifest'); await Bun.write('dist/manifest.json', JSON.stringify(createManifest())); console.timeEnd('manifest'); -console.time('html+css'); -await makeHTML('newtab', 'src/css/newtab.xcss'); -await makeHTML('settings', 'src/css/settings.xcss'); -console.timeEnd('html+css'); +console.time('html'); +await makeHTML('newtab'); +await makeHTML('settings'); +console.timeEnd('html'); + +console.time('css'); +await makeCSS('newtab', 'src/css/newtab.xcss'); +await makeCSS('settings', 'src/css/settings.xcss'); +console.timeEnd('css'); console.time('themes'); await makeThemes(); console.timeEnd('themes'); // New Tab & Settings apps -console.time('build'); -const out = await Bun.build({ +console.time('build:1'); +const out1 = await Bun.build({ entrypoints: ['src/newtab.ts', 'src/settings.ts'], outdir: 'dist', target: 'browser', minify: !dev, sourcemap: dev ? 'linked' : 'none', }); -console.timeEnd('build'); -console.log(out); +console.timeEnd('build:1'); +console.log(out1.outputs); +if (!out1.success) throw new AggregateError(out1.logs, 'Build failed'); // Background service worker script -console.time('build2'); +console.time('build:2'); const out2 = await Bun.build({ entrypoints: ['src/sw.ts'], outdir: 'dist', @@ -151,13 +171,13 @@ const out2 = await Bun.build({ minify: !dev, sourcemap: dev ? 'linked' : 'none', }); -console.timeEnd('build2'); -console.log(out2); +console.timeEnd('build:2'); +console.log(out2.outputs); +if (!out2.success) throw new AggregateError(out2.logs, 'Build failed'); if (!dev) { - console.time('minify'); - await minifyJS(out.outputs[0]); - await minifyJS(out.outputs[1]); - await minifyJS(out2.outputs[0]); - console.timeEnd('minify'); + console.time('minify:js'); + await minifyJS(out1.outputs); + await minifyJS(out2.outputs); + console.timeEnd('minify:js'); } From 1163df198a7e27e0e82155f25eaf4292b030436e Mon Sep 17 00:00:00 2001 From: Max Milton Date: Sat, 14 Dec 2024 13:13:50 +0900 Subject: [PATCH 09/15] chore: Minor tweaks --- src/settings.ts | 5 +++-- src/types.ts | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/settings.ts b/src/settings.ts index 8a9dbd11..63501ce7 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -14,6 +14,7 @@ interface SettingsState { type ItemIndex = [listIndex: 0 | 1, itemIndex: number]; +/** Section drag-and-drop helper functions */ interface SectionScope { indexOf(list: 0 | 1, item: SectionOrderItem): number; moveItem(from: ItemIndex, to: ItemIndex): void; @@ -24,7 +25,7 @@ const DEFAULT_THEME = 'auto'; // eslint-disable-next-line unicorn/prefer-top-level-await const themesData = fetch('themes.json').then( - (res) => res.json() as Promise, + (response) => response.json() as Promise, ); type SectionComponent = HTMLLIElement; @@ -137,7 +138,7 @@ const meta = compile(`

- DISPLAY ORDER + DISPLAY (in order)
    diff --git a/src/types.ts b/src/types.ts index 530224b1..777bc4bf 100644 --- a/src/types.ts +++ b/src/types.ts @@ -8,6 +8,9 @@ declare global { } } +/** JSON object with theme name keys and raw CSS code values. */ +export type ThemesData = Record; + export type SectionOrderItem = (typeof DEFAULT_SECTION_ORDER)[number]; export interface UserStorageData { @@ -20,6 +23,3 @@ export interface UserStorageData { /** Sections order user preference. */ o?: SectionOrderItem[]; } - -/** JSON object with theme name keys and raw CSS code values. */ -export type ThemesData = Record; From 3204fada9a251abf20b295564cc52f05d28acdb7 Mon Sep 17 00:00:00 2001 From: Max Milton Date: Sat, 14 Dec 2024 13:16:05 +0900 Subject: [PATCH 10/15] feat: Use new stage1 fragment API --- src/newtab.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/newtab.ts b/src/newtab.ts index 3b889713..ffdcc986 100644 --- a/src/newtab.ts +++ b/src/newtab.ts @@ -1,7 +1,7 @@ // Theme loader code must run first import './theme'; -import { append, createFragment } from 'stage1'; +import { append, fragment } from 'stage1'; import { BookmarkBar } from './components/BookmarkBar'; import { Menu } from './components/Menu'; import { Search } from './components/Search'; @@ -9,16 +9,16 @@ import { handleClick, storage } from './utils'; performance.mark('Initialise Components'); -const frag = createFragment(); +const container = fragment(); // Create Search component first because it has asynchronous calls that can // execute while the remaining components are constructed -append(Search(), frag); +append(Search(), container); // Create BookmarkBar component near last because, after an async call, it needs // to synchronously and sequentially calculate DOM layout multiple times and // could cause reflow in extreme situations, so paint the rest of the app first -if (!storage.b) append(BookmarkBar(), frag); -append(Menu(), frag); -append(frag, document.body); +if (!storage.b) append(BookmarkBar(), container); +append(Menu(), container); +append(container, document.body); performance.measure('Initialise Components', 'Initialise Components'); From 7e23da2b7b2cf071406b93b6ee76d2ec611cf43d Mon Sep 17 00:00:00 2001 From: Max Milton Date: Sat, 14 Dec 2024 15:44:55 +0900 Subject: [PATCH 11/15] chore: Fix detecting service worker in e2e tests --- test/e2e/fixtures.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/fixtures.ts b/test/e2e/fixtures.ts index 4bf35c77..529961f2 100644 --- a/test/e2e/fixtures.ts +++ b/test/e2e/fixtures.ts @@ -13,7 +13,7 @@ export const test = baseTest.extend<{ async context({}, use) { const extensionPath = path.join(__dirname, '../../dist'); const context = await chromium.launchPersistentContext('', { - // headless: false, + headless: false, args: [ '--headless=new', // chromium 112+ // '--virtual-time-budget=5000', // chromium 112+, fast-forward timers From c1eef94d73af81fc4c92c61db077583313fba071 Mon Sep 17 00:00:00 2001 From: Max Milton Date: Sun, 15 Dec 2024 00:03:30 +0900 Subject: [PATCH 12/15] test: Validate HTML files --- bun.lockb | Bin 114212 -> 114588 bytes package.json | 2 +- test/unit/index.test.ts | 12 +++++++++++- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/bun.lockb b/bun.lockb index 30fb9bf1c710ff5a60db7a2cfbdd82c12e09f061..61d3bb853e3120a313191eea3c3d3c3637259ac0 100755 GIT binary patch delta 2395 zcmc&$dr(wW7{3R3>?IHp1lE-|3d>_*6Ffac^zU9i!>@_TK9qr)_p@lO#EVkt2-) zgRJ?z*^=Z9-36v~FM?@)J(%vd1B`Xt%2&bggu|jFq-HmLD1V?*t#ByziydFLvZ-oh zK?;wZ9c5RCQfKl9rg-@wtmiRD;-wx2*@6;NpzOIm(jd1fO2ze_1{;t0{5w;kya3gv zTPHMz8RQPA1yGFtXfoJDnD`sniSl~nZr(jK}iSgr)=LtN(j(XriYT*A%zh zHZQ5muH}VwI{n>keoLlZ7^n3=AI95Sk2b6yb~(R9${VeJXZ^hk2j4b$8rsXX_V*Q~~!eO13DZ7j22b@q#xVcT!K)>Gd4{{3?qDW10% ztiF1x^OdUOHTN%jf9}_N(@E4mcVl`({OR{@b&cM}0&nm^c3xYF~#lPwz-$zI=a&k*9V#^S`0|`HW5@3*bj^4dj2| z8pJcZjBG4FiR(DdZWviGH{lw>8*$Zg`KFPDHs#;EuDkUoSYK$8R!zM`z{_hL9!d`N zBwl#y@D$a-%q_u73E9CpZQw?dTJce<&Qg4%6`uoqvlX94^w1Jxl#qx7S%WvA1UR4r9E0-#c8jUz~_0cwF`z)_$MI1JFMPOm*34SN7OO11-A#T6~PH#Qb# zG>`yH1mb{rAO@HO7=UNR$D!=7nkuBqsX{tnb^%*}&0=#H3-&IAy8u`WfyYXp6Uz87ixA$hI%srg&=iQ++DZ{we%x1AVk#1&TtVygjvv|$m=iF)P zmK@vT4Gu_;)kTljO^{l|bu+7Ar6SY9D#oPZ3_&xLpX&CAx$^7mc3Y;`#p$9^uO(YN zv@kEt;P>3+C$ZNz@p{eUj2*&1m#JC3hzDr~{}4=$IH@Y?_UV0`*Cm!AA!zU)MAojP zjSVxu3VfWP${*OG)O>L&mrW5rrQuD+-c4(f)petLlE~zAcO-W&K*|MdP m1!X105zF&etX#B0e0Gk-s-JK~M*J&0=RDIlyEn5D4*vj%5bX#6 delta 2103 zcmd5-ZBSHY6uxKW>u!U}*Rm@D%J)Z$I>N4q*e(fyX!#K=3(K-FV~X17lrw$klxtIs zW-7jpFJL4oP1%T}t%XtK5F=AR%oeo9A5Ky_+K;ptn-MAKIqU`f+0V|*bD#5^^SS?m2eLZGKdIR{ZTZ=Q+ijm64t4ysH)!DZ_B&Op4)u`2 zmeu4)B;A>pz6;AQhKy5`op&o0Wd?YeBfvw@)1l4C8^O*hMKOU(!4cpH;Z!%&$Fsq(GsgLVUM*2e-2eF3Y`V&D7=*ub9%F_>I-PB28x8;Y1Lh<5)Z1EstLGX z0fMTqFR-dksPc&k-ejxVspUv`qpj*mbcg_^vkFF~iYZkM^~{ty0mW4UBkw`2o>IlG zKudw#L8z&Dx*?aZyLLG2^=Wvcd$117?`nJi%uCax@j-A1bTbDvX14_w9r%dGZQwBI z4>|eY7>oMoK%aem-~Xul5-lXl@&+ecMnjW2(5)3*Dy&Mkjd^wey= z>W`YUz3-Iw?%>b;Bu9=|?x&nzlb<&GY{KvZ!;Y_LR4o<-zY0EEn)IW@ zVt&gUi2*jl;b|FK44)&V9f$@pwU~_^*ln{+$fD{p?vy*532+-cpKrs*?0w)U&<=b6 z90QI69RMFkKC%Mfqy3J&kVUsrbK&Lz7QhND0`h^yKmo7>D3tzeI%?vsxNGi`5B)yi zP2erL`*BK-e-7adKqF8OGywRxD7AoFUc``yC2$LYrN9$_T?U(Jp*j;qc!$MEyP0D3 zW5|pH3dsgDC7Hg2+XL_m-w*5p-T>Z~Z5STzfm;Wx0#?gj6b$Z!drFR&=}D6db!!1P zkPT!3pCWx)R^(8#ngaVKc@+RdLT7g-Q z_?%CG<{ij>5b|7y&j@e5BCs9cTA{Kjj}oUR!M)Ba06SJEyYnbZUeBXuvUI&-q1<$` zNSBLd%Nw;6so#Mg4im fKyYtQ*P422s|>c}Y+L`T)Ssh8U3<^b%+P-U{Vbb1 diff --git a/package.json b/package.json index c36bd24a..fb87a1aa 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "@eslint/js": "9.17.0", "@maxmilton/eslint-config": "0.0.8", "@maxmilton/stylelint-config": "0.1.2", - "@maxmilton/test-utils": "0.0.6", + "@maxmilton/test-utils": "0.0.7", "@playwright/test": "1.49.1", "@types/bun": "1.1.14", "@types/chrome": "0.0.287", diff --git a/test/unit/index.test.ts b/test/unit/index.test.ts index 343d5c2b..12871f86 100644 --- a/test/unit/index.test.ts +++ b/test/unit/index.test.ts @@ -1,5 +1,6 @@ import { describe, expect, test } from 'bun:test'; import { readdir } from 'node:fs/promises'; +import { validate } from '@maxmilton/test-utils/html'; describe('dist files', () => { // FIXME: The bun file type is just inferred from the file extension, not the @@ -49,8 +50,17 @@ describe('dist files', () => { const distDir = await readdir('dist'); expect(distDir).toHaveLength(distFiles.length); }); + + test.each(distFiles.filter(([filename]) => filename.endsWith('.html')))( + '%s contains valid HTML', + async (filename) => { + const file = Bun.file(`dist/${filename}`); + const html = await file.text(); + const result = validate(html); + expect(result.valid).toBeTrue(); + }, + ); }); -// TODO: HTML files should be valid HTML // TODO: HTML files have correct title // TODO: HTML files have correct JS and CSS file references From 28b14198b76c04bc4dbc5dcca0bbec8e03696431 Mon Sep 17 00:00:00 2001 From: Max Milton Date: Sun, 15 Dec 2024 00:04:30 +0900 Subject: [PATCH 13/15] test: Better service worker e2e tests --- test/e2e/fixtures.ts | 6 +++--- test/e2e/sw.spec.ts | 14 ++++++-------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/test/e2e/fixtures.ts b/test/e2e/fixtures.ts index 529961f2..404e87f8 100644 --- a/test/e2e/fixtures.ts +++ b/test/e2e/fixtures.ts @@ -28,11 +28,11 @@ export const test = baseTest.extend<{ await context.close(); }, async extensionId({ context }, use) { - let [background] = context.serviceWorkers(); + let [sw] = context.serviceWorkers(); // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - background ??= await context.waitForEvent('serviceworker'); + sw ??= await context.waitForEvent('serviceworker', { timeout: 200 }); - const extensionId = background.url().split('/')[2]; + const extensionId = sw.url().split('/')[2]; await use(extensionId); }, }); diff --git a/test/e2e/sw.spec.ts b/test/e2e/sw.spec.ts index 47b20940..f25018b8 100644 --- a/test/e2e/sw.spec.ts +++ b/test/e2e/sw.spec.ts @@ -1,11 +1,9 @@ import { expect, test } from './fixtures'; -test('background service worker', async ({ context }) => { - let [background] = context.serviceWorkers(); - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - background ??= await context.waitForEvent('serviceworker'); - - // FIXME: Better assertions - - expect(background).toBeTruthy(); +test('has a single background service worker (sw.js)', async ({ context, extensionId }) => { + const workers = context.serviceWorkers(); + expect(workers).toHaveLength(1); + expect(workers[0]?.url()).toBe(`chrome-extension://${extensionId}/sw.js`); }); + +// TODO: Check there are no console messages or unhandled errors in the worker. From 82495d579f2efd3afc5e6290b78fac4bace1a4f3 Mon Sep 17 00:00:00 2001 From: Max Milton Date: Sun, 15 Dec 2024 00:04:48 +0900 Subject: [PATCH 14/15] test: Better condition check --- test/unit/index.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/index.test.ts b/test/unit/index.test.ts index 12871f86..6621dbe6 100644 --- a/test/unit/index.test.ts +++ b/test/unit/index.test.ts @@ -35,7 +35,7 @@ describe('dist files', () => { expect(file.type).toBe(type); // TODO: Keep this? Type seems to be resolved from the file extension, not the file data. }); - if (minBytes != null && maxBytes != null) { + if (minBytes !== undefined && maxBytes !== undefined) { test('is within expected file size limits', () => { expect.assertions(2); expect(file.size).toBeGreaterThan(minBytes); From f08c9bc883d4f4c6ab29613eb1d8722531b95eb9 Mon Sep 17 00:00:00 2001 From: Max Milton Date: Thu, 2 Jan 2025 12:37:43 +0900 Subject: [PATCH 15/15] chore: Update dependencies --- bun.lockb | Bin 114588 -> 114252 bytes package.json | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bun.lockb b/bun.lockb index 61d3bb853e3120a313191eea3c3d3c3637259ac0..87d2f129ecfad42f95eb423ea2b73398967b64f9 100755 GIT binary patch delta 8182 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 N-me1ha