diff --git a/addons/addon-web-fonts/test/WebFontsAddon.test.ts b/addons/addon-web-fonts/test/WebFontsAddon.test.ts index 09bb01a9..1315b1ee 100644 --- a/addons/addon-web-fonts/test/WebFontsAddon.test.ts +++ b/addons/addon-web-fonts/test/WebFontsAddon.test.ts @@ -6,7 +6,6 @@ import test from '@playwright/test'; import { deepStrictEqual, strictEqual } from 'assert'; import { ITestContext, createTestContext, openTerminal, pollFor, timeout } from '../../../test/playwright/TestUtils'; - let ctx: ITestContext; test.beforeAll(async ({ browser }) => { ctx = await createTestContext(browser); @@ -15,5 +14,107 @@ test.beforeAll(async ({ browser }) => { test.afterAll(async () => await ctx.page.close()); test.describe('WebFontsAddon', () => { - test('nothing', () => {}); + + test.beforeEach(async () => { + // make sure that we start with no webfonts in the document + const empty = await await getDocumentFonts(); + deepStrictEqual(empty, []); + }); + test.afterEach(async () => { + // for font loading tests to work, we have to remove added rules and fonts + // to work around the quite aggressive font caching done by the browsers + await ctx.page.evaluate(` + document.styleSheets[0].deleteRule(1); + document.styleSheets[0].deleteRule(0); + document.fonts.clear(); + `); + }); + + test.describe('font loading at runtime', () => { + test('loadFonts (JS)', async () => { + await ctx.page.evaluate(` + const ff1 = new FontFace('Kongtext', "url(/kongtext.regular.ttf) format('truetype')"); + const ff2 = new FontFace('BPdots', "url(/bpdots.regular.otf) format('opentype')"); + loadFonts([ff1, ff2]); + `); + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }, { family: 'BPdots', status: 'loaded' }]); + }); + test('loadFonts (CSS, unquoted)', async () => { + await ctx.page.evaluate(` + document.styleSheets[0].insertRule("@font-face {font-family: Kongtext; src: url(/kongtext.regular.ttf) format('truetype')}", 0); + document.styleSheets[0].insertRule("@font-face {font-family: BPdots; src: url(/bpdots.regular.otf) format('opentype')}", 1); + loadFonts(['Kongtext', 'BPdots']); + `); + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }, { family: 'BPdots', status: 'loaded' }]); + }); + test('loadFonts (CSS, quoted)', async ({ browser }) => { + // NOTE: firefox preserves family quotes from CSS rules in fontface, all other browsers unquote them + await ctx.page.evaluate(` + document.styleSheets[0].insertRule("@font-face {font-family: 'Kongtext'; src: url(/kongtext.regular.ttf) format('truetype')}", 0); + document.styleSheets[0].insertRule("@font-face {font-family: 'BPdots'; src: url(/bpdots.regular.otf) format('opentype')}", 1); + loadFonts(['Kongtext', 'BPdots']); + `); + if (browser.browserType().name() === 'firefox') { + deepStrictEqual(await getDocumentFonts(), [{ family: '"Kongtext"', status: 'loaded' }, { family: '"BPdots"', status: 'loaded' }]); + } else { + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }, { family: 'BPdots', status: 'loaded' }]); + } + }); + test('FontFace hashing', async () => { + // multiple calls of `loadFonts` with the same objects shall not bloat document.fonts + await ctx.page.evaluate(` + const ff1 = new FontFace('Kongtext', "url(/kongtext.regular.ttf) format('truetype')"); + const ff2 = new FontFace('BPdots', "url(/bpdots.regular.otf) format('opentype')"); + loadFonts([ff1, ff2]); + loadFonts([ff1, ff2]); + loadFonts([ff1, ff2]).then(() => loadFonts([ff1, ff2])); + `); + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }, { family: 'BPdots', status: 'loaded' }]); + }); + + test('autoload & relayout from ctor', async ({ browser }) => { + // to make this test work, we exclude the default measurement char W (x57) by restricting unicode-range + // now the browser will postpone font loading until codepoint is hit --> wrong glyph metrics on first usage + const data = await ctx.page.evaluate(` + document.styleSheets[0].insertRule("@font-face {font-family: Kongtext; src: url(/kongtext.regular.ttf) format('truetype'); unicode-range: U+00A0-00FF}", 0); + `); + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'unloaded' }]); + + // broken case: webfont in ctor without addon usage + await ctx.page.evaluate(` + window.helperTerm = new Terminal({fontFamily: '"Kongtext", ' + term.options.fontFamily}); + window.helperTerm.open(term.element); + `); + + // safari loads the font, firefox & chrome dont + if (browser.browserType().name() === 'webkit') { + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }]); + } else { + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'unloaded' }]); + } + + // good case: addon fixes layout for webfont in ctor + // the relayout happens async, so wait a bit with a promise + await ctx.page.evaluate(` + window.helperTerm.dispose(); + window.helperTerm = new Terminal({fontFamily: '"Kongtext", ' + term.options.fontFamily}); + window._webfontsAddon = new WebFontsAddon(); + window.helperTerm.loadAddon(window._webfontsAddon); + window.helperTerm.open(term.element); + `); + await timeout(100); + deepStrictEqual(await getDocumentFonts(), [{ family: 'Kongtext', status: 'loaded' }]); + + // cleanup this messy test case + await ctx.page.evaluate(` + window.helperTerm.dispose(); + window._webfontsAddon.dispose(); + `); + }); + }); + }); + +async function getDocumentFonts(): Promise { + return ctx.page.evaluate(`Array.from(document.fonts).map(ff => ({family: ff.family, status: ff.status}))`); +}