From bd605245aae4c8545bdd38a597b89ad78ca3d978 Mon Sep 17 00:00:00 2001 From: Tim Neutkens Date: Mon, 22 Jan 2024 17:24:39 +0100 Subject: [PATCH] Skip @next/font tests in Turbopack as next/font is supported (#60982) ## What? Skip the `@next/font` legacy tests in Turbopack. `next/font` is supported with Turbopack enabled. Closes NEXT-2183 --- test/e2e/app-dir/next-font/next-font.test.ts | 786 ++++++------ test/e2e/next-font/index.test.ts | 1170 +++++++++--------- 2 files changed, 986 insertions(+), 970 deletions(-) diff --git a/test/e2e/app-dir/next-font/next-font.test.ts b/test/e2e/app-dir/next-font/next-font.test.ts index 92378b434466d..d643e5387050f 100644 --- a/test/e2e/app-dir/next-font/next-font.test.ts +++ b/test/e2e/app-dir/next-font/next-font.test.ts @@ -24,428 +24,438 @@ const getAttrs = (elems: Cheerio) => .sort((a, b) => (a.href < b.href ? -1 : 1)) describe('app dir - next/font', () => { - describe.each([['app'], ['app-old']])('%s', (fixture: string) => { - createNextDescribe( - 'app dir - next-font', - { - files: { - app: new FileRef(join(__dirname, fixture)), - fonts: new FileRef(join(__dirname, 'fonts')), - node_modules: new FileRef(join(__dirname, 'node_modules')), - 'next.config.js': new FileRef(join(__dirname, 'next.config.js')), - }, - dependencies: { - '@next/font': 'canary', - }, - skipDeployment: true, - }, - ({ next, isNextDev: isDev }) => { - describe('import values', () => { - it('should have correct values at /', async () => { - const $ = await next.render$('/') - - // layout - expect(JSON.parse($('#root-layout').text())).toEqual({ - className: expect.stringMatching(/^__className_.{6}$/), - variable: expect.stringMatching(/^__variable_.{6}$/), - style: { - fontFamily: expect.stringMatching(/^'__font1_.{6}'$/), - }, - }) - // page - expect(JSON.parse($('#root-page').text())).toEqual({ - className: expect.stringMatching(/^__className_.{6}$/), - variable: expect.stringMatching(/^__variable_.{6}$/), - style: { - fontFamily: expect.stringMatching(/^'__font2_.{6}'$/), - }, - }) - // Comp - expect(JSON.parse($('#root-comp').text())).toEqual({ - className: expect.stringMatching(/^__className_.{6}$/), - style: { - fontFamily: expect.stringMatching(/^'__font3_.{6}'$/), - fontStyle: 'italic', - fontWeight: 900, - }, - }) - }) - - it('should have correct values at /client', async () => { - const $ = await next.render$('/client') - - // root layout - expect(JSON.parse($('#root-layout').text())).toEqual({ - className: expect.stringMatching(/^__className_.{6}$/), - variable: expect.stringMatching(/^__variable_.{6}$/), - style: { - fontFamily: expect.stringMatching(/^'__font1_.{6}'$/), - }, - }) - - // layout - expect(JSON.parse($('#client-layout').text())).toEqual({ - className: expect.stringMatching(/^__className_.{6}$/), - style: { - fontFamily: expect.stringMatching(/^'__font4_.{6}'$/), - fontWeight: 100, - }, - }) - // page - expect(JSON.parse($('#client-page').text())).toEqual({ - className: expect.stringMatching(/^__className_.{6}$/), - style: { - fontFamily: expect.stringMatching(/^'__font5_.{6}'$/), - fontStyle: 'italic', - }, - }) - // Comp - expect(JSON.parse($('#client-comp').text())).toEqual({ - className: expect.stringMatching(/^__className_.{6}$/), - style: { - fontFamily: expect.stringMatching(/^'__font6_.{6}'$/), - }, - }) - }) - - it('should transform code in node_modules', async () => { - const $ = await next.render$('/third-party') - expect(JSON.parse($('#third-party-page').text())).toEqual({ - className: '__className_3953ad', - style: { - fontFamily: "'__font1_3953ad'", - }, - variable: '__variable_3953ad', - }) - }) - }) - - describe('computed styles', () => { - it('should have correct styles at /', async () => { - const browser = await next.browser('/') - - // layout - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#root-layout")).fontFamily' - ) - ).toMatch(/^__font1_.{6}$/) - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#root-layout")).fontWeight' - ) - ).toBe('400') - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#root-layout")).fontStyle' - ) - ).toBe('normal') - - // page - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#root-page")).fontFamily' - ) - ).toMatch(/^__font2_.{6}$/) - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#root-page")).fontWeight' - ) - ).toBe('400') - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#root-page")).fontStyle' - ) - ).toBe('normal') - - // Comp - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#root-comp")).fontFamily' - ) - ).toMatch(/^__font3_.{6}$/) - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#root-comp")).fontWeight' - ) - ).toBe('900') - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#root-comp")).fontStyle' - ) - ).toBe('italic') - }) - - it('should have correct styles at /client', async () => { - const browser = await next.browser('/client') - - // root layout - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#root-layout")).fontFamily' - ) - ).toMatch(/^__font1_.{6}$/) - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#root-layout")).fontWeight' - ) - ).toBe('400') - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#root-layout")).fontStyle' - ) - ).toBe('normal') - - // layout - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#client-layout")).fontFamily' - ) - ).toMatch(/^__font4_.{6}$/) - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#client-layout")).fontWeight' - ) - ).toBe('100') - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#client-layout")).fontStyle' - ) - ).toBe('normal') - - // page - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#client-page")).fontFamily' - ) - ).toMatch(/^__font5_.{6}$/) - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#client-page")).fontWeight' - ) - ).toBe('400') - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#client-page")).fontStyle' - ) - ).toBe('italic') - - // Comp - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#client-comp")).fontFamily' - ) - ).toMatch(/^__font6_.{6}$/) - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#client-comp")).fontWeight' - ) - ).toBe('400') - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#client-comp")).fontStyle' - ) - ).toBe('normal') - }) - }) + for (const fixture of ['app', 'app-old']) { + describe(fixture, () => { + // Turbopack only support `next/font` as `@next/font` is going to be removed in the next major version. + if (process.env.TURBOPACK && fixture === 'app-old') { + return + } - if (!isDev) { - describe('preload', () => { - it('should preload correctly with server components', async () => { + createNextDescribe( + 'app dir - next-font', + { + files: { + app: new FileRef(join(__dirname, fixture)), + fonts: new FileRef(join(__dirname, 'fonts')), + node_modules: new FileRef(join(__dirname, 'node_modules')), + 'next.config.js': new FileRef(join(__dirname, 'next.config.js')), + }, + dependencies: { + '@next/font': 'canary', + }, + skipDeployment: true, + }, + ({ next, isNextDev: isDev }) => { + describe('import values', () => { + it('should have correct values at /', async () => { const $ = await next.render$('/') - // Preconnect - expect($('link[rel="preconnect"]').length).toBe(0) - - // From root layout - containObjects(getAttrs($('link[as="font"]')), [ - { - as: 'font', - crossorigin: '', - href: '/_next/static/media/b2104791981359ae-s.p.woff2', - rel: 'preload', - type: 'font/woff2', + // layout + expect(JSON.parse($('#root-layout').text())).toEqual({ + className: expect.stringMatching(/^__className_.{6}$/), + variable: expect.stringMatching(/^__variable_.{6}$/), + style: { + fontFamily: expect.stringMatching(/^'__font1_.{6}'$/), }, - { - as: 'font', - crossorigin: '', - href: '/_next/static/media/b61859a50be14c53-s.p.woff2', - rel: 'preload', - type: 'font/woff2', + }) + // page + expect(JSON.parse($('#root-page').text())).toEqual({ + className: expect.stringMatching(/^__className_.{6}$/), + variable: expect.stringMatching(/^__variable_.{6}$/), + style: { + fontFamily: expect.stringMatching(/^'__font2_.{6}'$/), }, - { - as: 'font', - crossorigin: '', - href: '/_next/static/media/e9b9dc0d8ba35f48-s.p.woff2', - rel: 'preload', - type: 'font/woff2', + }) + // Comp + expect(JSON.parse($('#root-comp').text())).toEqual({ + className: expect.stringMatching(/^__className_.{6}$/), + style: { + fontFamily: expect.stringMatching(/^'__font3_.{6}'$/), + fontStyle: 'italic', + fontWeight: 900, }, - ]) + }) }) - it('should preload correctly with client components', async () => { + it('should have correct values at /client', async () => { const $ = await next.render$('/client') - // Preconnect - expect($('link[rel="preconnect"]').length).toBe(0) - - // From root layout - containObjects(getAttrs($('link[as="font"]')), [ - { - as: 'font', - crossorigin: '', - href: '/_next/static/media/e1053f04babc7571-s.p.woff2', - rel: 'preload', - type: 'font/woff2', + // root layout + expect(JSON.parse($('#root-layout').text())).toEqual({ + className: expect.stringMatching(/^__className_.{6}$/), + variable: expect.stringMatching(/^__variable_.{6}$/), + style: { + fontFamily: expect.stringMatching(/^'__font1_.{6}'$/), }, - { - as: 'font', - crossorigin: '', - href: '/_next/static/media/e9b9dc0d8ba35f48-s.p.woff2', - rel: 'preload', - type: 'font/woff2', + }) + + // layout + expect(JSON.parse($('#client-layout').text())).toEqual({ + className: expect.stringMatching(/^__className_.{6}$/), + style: { + fontFamily: expect.stringMatching(/^'__font4_.{6}'$/), + fontWeight: 100, }, - { - as: 'font', - crossorigin: '', - href: '/_next/static/media/feab2c68f2a8e9a4-s.p.woff2', - rel: 'preload', - type: 'font/woff2', + }) + // page + expect(JSON.parse($('#client-page').text())).toEqual({ + className: expect.stringMatching(/^__className_.{6}$/), + style: { + fontFamily: expect.stringMatching(/^'__font5_.{6}'$/), + fontStyle: 'italic', }, - ]) + }) + // Comp + expect(JSON.parse($('#client-comp').text())).toEqual({ + className: expect.stringMatching(/^__className_.{6}$/), + style: { + fontFamily: expect.stringMatching(/^'__font6_.{6}'$/), + }, + }) }) - it('should preload correctly with layout using fonts', async () => { - const $ = await next.render$('/layout-with-fonts') - - // Preconnect - expect($('link[rel="preconnect"]').length).toBe(0) - - // From root layout - containObjects(getAttrs($('link[as="font"]')), [ - { - as: 'font', - crossorigin: '', - href: '/_next/static/media/75c5faeeb9c86969-s.p.woff2', - rel: 'preload', - type: 'font/woff2', + it('should transform code in node_modules', async () => { + const $ = await next.render$('/third-party') + expect(JSON.parse($('#third-party-page').text())).toEqual({ + className: '__className_3953ad', + style: { + fontFamily: "'__font1_3953ad'", }, - { - as: 'font', - crossorigin: '', - href: '/_next/static/media/e9b9dc0d8ba35f48-s.p.woff2', - rel: 'preload', - type: 'font/woff2', - }, - ]) + variable: '__variable_3953ad', + }) }) + }) - it('should preload correctly with page using fonts', async () => { - const $ = await next.render$('/page-with-fonts') + describe('computed styles', () => { + it('should have correct styles at /', async () => { + const browser = await next.browser('/') - // Preconnect - expect($('link[rel="preconnect"]').length).toBe(0) + // layout + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#root-layout")).fontFamily' + ) + ).toMatch(/^__font1_.{6}$/) + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#root-layout")).fontWeight' + ) + ).toBe('400') + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#root-layout")).fontStyle' + ) + ).toBe('normal') + + // page + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#root-page")).fontFamily' + ) + ).toMatch(/^__font2_.{6}$/) + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#root-page")).fontWeight' + ) + ).toBe('400') + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#root-page")).fontStyle' + ) + ).toBe('normal') + + // Comp + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#root-comp")).fontFamily' + ) + ).toMatch(/^__font3_.{6}$/) + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#root-comp")).fontWeight' + ) + ).toBe('900') + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#root-comp")).fontStyle' + ) + ).toBe('italic') + }) - // From root layout - containObjects(getAttrs($('link[as="font"]')), [ - { - as: 'font', - crossorigin: '', - href: '/_next/static/media/568e4c6d8123c4d6-s.p.woff2', - rel: 'preload', - type: 'font/woff2', - }, - { - as: 'font', - crossorigin: '', - href: '/_next/static/media/e9b9dc0d8ba35f48-s.p.woff2', - rel: 'preload', - type: 'font/woff2', - }, - ]) + it('should have correct styles at /client', async () => { + const browser = await next.browser('/client') + + // root layout + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#root-layout")).fontFamily' + ) + ).toMatch(/^__font1_.{6}$/) + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#root-layout")).fontWeight' + ) + ).toBe('400') + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#root-layout")).fontStyle' + ) + ).toBe('normal') + + // layout + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#client-layout")).fontFamily' + ) + ).toMatch(/^__font4_.{6}$/) + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#client-layout")).fontWeight' + ) + ).toBe('100') + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#client-layout")).fontStyle' + ) + ).toBe('normal') + + // page + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#client-page")).fontFamily' + ) + ).toMatch(/^__font5_.{6}$/) + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#client-page")).fontWeight' + ) + ).toBe('400') + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#client-page")).fontStyle' + ) + ).toBe('italic') + + // Comp + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#client-comp")).fontFamily' + ) + ).toMatch(/^__font6_.{6}$/) + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#client-comp")).fontWeight' + ) + ).toBe('400') + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#client-comp")).fontStyle' + ) + ).toBe('normal') }) }) - describe('preconnect', () => { - it.each([['page'], ['layout'], ['component']])( - 'should add preconnect when preloading is disabled in %s', - async (type: string) => { - const $ = await next.render$(`/preconnect-${type}`) + if (!isDev) { + describe('preload', () => { + it('should preload correctly with server components', async () => { + const $ = await next.render$('/') // Preconnect - expect($('link[rel="preconnect"]').length).toBe(1) - expect($('link[rel="preconnect"]').get(0).attribs).toEqual({ - crossorigin: '', - href: '/', - rel: 'preconnect', - }) - // Preload - expect($('link[as="font"]').length).toBe(0) - } - ) + expect($('link[rel="preconnect"]').length).toBe(0) + + // From root layout + containObjects(getAttrs($('link[as="font"]')), [ + { + as: 'font', + crossorigin: '', + href: '/_next/static/media/b2104791981359ae-s.p.woff2', + rel: 'preload', + type: 'font/woff2', + }, + { + as: 'font', + crossorigin: '', + href: '/_next/static/media/b61859a50be14c53-s.p.woff2', + rel: 'preload', + type: 'font/woff2', + }, + { + as: 'font', + crossorigin: '', + href: '/_next/static/media/e9b9dc0d8ba35f48-s.p.woff2', + rel: 'preload', + type: 'font/woff2', + }, + ]) + }) + + it('should preload correctly with client components', async () => { + const $ = await next.render$('/client') - it('should not preconnect when css is used but no fonts', async () => { - const $ = await next.render$('/no-preconnect') + // Preconnect + expect($('link[rel="preconnect"]').length).toBe(0) + + // From root layout + containObjects(getAttrs($('link[as="font"]')), [ + { + as: 'font', + crossorigin: '', + href: '/_next/static/media/e1053f04babc7571-s.p.woff2', + rel: 'preload', + type: 'font/woff2', + }, + { + as: 'font', + crossorigin: '', + href: '/_next/static/media/e9b9dc0d8ba35f48-s.p.woff2', + rel: 'preload', + type: 'font/woff2', + }, + { + as: 'font', + crossorigin: '', + href: '/_next/static/media/feab2c68f2a8e9a4-s.p.woff2', + rel: 'preload', + type: 'font/woff2', + }, + ]) + }) + + it('should preload correctly with layout using fonts', async () => { + const $ = await next.render$('/layout-with-fonts') + + // Preconnect + expect($('link[rel="preconnect"]').length).toBe(0) + + // From root layout + containObjects(getAttrs($('link[as="font"]')), [ + { + as: 'font', + crossorigin: '', + href: '/_next/static/media/75c5faeeb9c86969-s.p.woff2', + rel: 'preload', + type: 'font/woff2', + }, + { + as: 'font', + crossorigin: '', + href: '/_next/static/media/e9b9dc0d8ba35f48-s.p.woff2', + rel: 'preload', + type: 'font/woff2', + }, + ]) + }) + + it('should preload correctly with page using fonts', async () => { + const $ = await next.render$('/page-with-fonts') - // Preconnect - expect($('link[rel="preconnect"]').length).toBe(0) - // Preload - expect(getAttrs($('link[as="font"]'))).toEqual([]) + // Preconnect + expect($('link[rel="preconnect"]').length).toBe(0) + + // From root layout + containObjects(getAttrs($('link[as="font"]')), [ + { + as: 'font', + crossorigin: '', + href: '/_next/static/media/568e4c6d8123c4d6-s.p.woff2', + rel: 'preload', + type: 'font/woff2', + }, + { + as: 'font', + crossorigin: '', + href: '/_next/static/media/e9b9dc0d8ba35f48-s.p.woff2', + rel: 'preload', + type: 'font/woff2', + }, + ]) + }) }) - }) - } - describe('navigation', () => { - it('should not have duplicate preload tags on navigation', async () => { - const browser = await next.browser('/navigation') - - // Before navigation, root layout imports the font - const preloadBeforeNavigation = await browser.elementsByCss( - 'link[as="font"]' - ) - expect(preloadBeforeNavigation.length).toBe(1) - expect(await preloadBeforeNavigation[0].getAttribute('href')).toBe( - '/_next/static/media/c287665b44f047d4-s.p.woff2' - ) - - // Navigate to a page that also imports that font - await browser.elementByCss('a').click() - await browser.waitForElementByCss('#page-with-same-font') - - // After navigating - const preloadAfterNavigation = await browser.elementsByCss( - 'link[as="font"]' - ) - expect(preloadAfterNavigation.length).toBe(1) - expect(await preloadAfterNavigation[0].getAttribute('href')).toBe( - '/_next/static/media/c287665b44f047d4-s.p.woff2' - ) - }) - }) + describe('preconnect', () => { + it.each([['page'], ['layout'], ['component']])( + 'should add preconnect when preloading is disabled in %s', + async (type: string) => { + const $ = await next.render$(`/preconnect-${type}`) + + // Preconnect + expect($('link[rel="preconnect"]').length).toBe(1) + expect($('link[rel="preconnect"]').get(0).attribs).toEqual({ + crossorigin: '', + href: '/', + rel: 'preconnect', + }) + // Preload + expect($('link[as="font"]').length).toBe(0) + } + ) - if (isDev) { - describe('Dev errors', () => { - it('should recover on font loader error', async () => { - const browser = await next.browser('/') - const font1Content = await next.readFile('fonts/index.js') + it('should not preconnect when css is used but no fonts', async () => { + const $ = await next.render$('/no-preconnect') + + // Preconnect + expect($('link[rel="preconnect"]').length).toBe(0) + // Preload + expect(getAttrs($('link[as="font"]'))).toEqual([]) + }) + }) + } - // Break file - await next.patchFile( - 'fonts/index.js', - font1Content.replace('./font1.woff2', './does-not-exist.woff2') + describe('navigation', () => { + it('should not have duplicate preload tags on navigation', async () => { + const browser = await next.browser('/navigation') + + // Before navigation, root layout imports the font + const preloadBeforeNavigation = await browser.elementsByCss( + 'link[as="font"]' ) - expect(await hasRedbox(browser)).toBeTrue() - expect(await getRedboxSource(browser)).toInclude( - "Can't resolve './does-not-exist.woff2'" + expect(preloadBeforeNavigation.length).toBe(1) + expect( + await preloadBeforeNavigation[0].getAttribute('href') + ).toBe('/_next/static/media/c287665b44f047d4-s.p.woff2') + + // Navigate to a page that also imports that font + await browser.elementByCss('a').click() + await browser.waitForElementByCss('#page-with-same-font') + + // After navigating + const preloadAfterNavigation = await browser.elementsByCss( + 'link[as="font"]' + ) + expect(preloadAfterNavigation.length).toBe(1) + expect(await preloadAfterNavigation[0].getAttribute('href')).toBe( + '/_next/static/media/c287665b44f047d4-s.p.woff2' ) - - // Fix file - await next.patchFile('fonts/index.js', font1Content) - await browser.waitForElementByCss('#root-page') }) }) + + if (isDev) { + describe('Dev errors', () => { + it('should recover on font loader error', async () => { + const browser = await next.browser('/') + const font1Content = await next.readFile('fonts/index.js') + + // Break file + await next.patchFile( + 'fonts/index.js', + font1Content.replace( + './font1.woff2', + './does-not-exist.woff2' + ) + ) + expect(await hasRedbox(browser)).toBeTrue() + expect(await getRedboxSource(browser)).toInclude( + "Can't resolve './does-not-exist.woff2'" + ) + + // Fix file + await next.patchFile('fonts/index.js', font1Content) + await browser.waitForElementByCss('#root-page') + }) + }) + } } - } - ) - }) + ) + }) + } }) diff --git a/test/e2e/next-font/index.test.ts b/test/e2e/next-font/index.test.ts index 626b71a5c2bd4..5efa7d8121244 100644 --- a/test/e2e/next-font/index.test.ts +++ b/test/e2e/next-font/index.test.ts @@ -17,623 +17,629 @@ function getClassNameRegex(className: string): RegExp { } describe('next/font', () => { - describe.each([['app'], ['app-old']])('%s', (fixture: string) => { - let next: NextInstance - - if ((global as any).isNextDeploy) { - it('should skip next deploy for now', () => {}) - return - } - - beforeAll(async () => { - next = await createNext({ - files: { - pages: new FileRef(join(__dirname, `${fixture}/pages`)), - components: new FileRef(join(__dirname, `${fixture}/components`)), - fonts: new FileRef(join(__dirname, `${fixture}/fonts`)), - }, - dependencies: { - '@next/font': 'canary', - }, - env: { - NEXT_FONT_GOOGLE_MOCKED_RESPONSES: mockedGoogleFontResponses, - }, - }) - }) - afterAll(() => next.destroy()) - - if ((global as any).isNextDev) { - it('should use production cache control for fonts', async () => { - const html = await next.render('/') - console.log({ html }) - const $ = await next.render$('/') - const link = $('[rel="preload"][as="font"]').attr('href') - expect(link).toBeDefined() - const res = await next.fetch(link) - expect(res.headers.get('cache-control')).toBe( - 'public, max-age=31536000, immutable' - ) - }) - } - - describe('import values', () => { - test('page with font', async () => { - const html = await renderViaHTTP(next.url, '/with-fonts') - const $ = cheerio.load(html) - - // _app.js - expect(JSON.parse($('#app-open-sans').text())).toEqual({ - className: expect.stringMatching(getClassNameRegex('className')), - variable: expect.stringMatching(getClassNameRegex('variable')), - style: { - fontFamily: expect.stringMatching( - /^'__Open_Sans_.{6}', '__Open_Sans_Fallback_.{6}'$/ - ), - fontStyle: 'normal', + for (const fixture of ['app', 'app-old']) { + describe(fixture, () => { + // Turbopack only support `next/font` as `@next/font` is going to be removed in the next major version. + if (process.env.TURBOPACK && fixture === 'app-old') { + return + } + let next: NextInstance + + if ((global as any).isNextDeploy) { + it('should skip next deploy for now', () => {}) + return + } + + beforeAll(async () => { + next = await createNext({ + files: { + pages: new FileRef(join(__dirname, `${fixture}/pages`)), + components: new FileRef(join(__dirname, `${fixture}/components`)), + fonts: new FileRef(join(__dirname, `${fixture}/fonts`)), }, - }) - - // with-fonts.js - expect(JSON.parse($('#with-fonts-open-sans').text())).toEqual({ - className: expect.stringMatching(getClassNameRegex('className')), - variable: expect.stringMatching(getClassNameRegex('variable')), - style: { - fontFamily: expect.stringMatching( - /^'__Open_Sans_.{6}', '__Open_Sans_Fallback_.{6}'$/ - ), - fontStyle: 'normal', + dependencies: { + '@next/font': 'canary', }, - }) - - // CompWithFonts.js - expect(JSON.parse($('#comp-with-fonts-inter').text())).toEqual({ - className: expect.stringMatching(getClassNameRegex('className')), - style: { - fontFamily: expect.stringMatching( - /^'__Inter_.{6}', '__Inter_Fallback_.{6}'$/ - ), - fontWeight: 900, - fontStyle: 'normal', - }, - }) - expect(JSON.parse($('#comp-with-fonts-roboto').text())).toEqual({ - className: expect.stringMatching(getClassNameRegex('className')), - style: { - fontFamily: expect.stringMatching( - /^'__Roboto_.{6}', '__Roboto_Fallback_.{6}'$/ - ), - fontStyle: 'italic', - fontWeight: 100, + env: { + NEXT_FONT_GOOGLE_MOCKED_RESPONSES: mockedGoogleFontResponses, }, }) }) - - test('page with local fonts', async () => { - const html = await renderViaHTTP(next.url, '/with-local-fonts') - const $ = cheerio.load(html) - - // _app.js - expect(JSON.parse($('#app-open-sans').text())).toEqual({ - className: expect.stringMatching(getClassNameRegex('className')), - variable: expect.stringMatching(getClassNameRegex('variable')), - style: { - fontFamily: expect.stringMatching( - /^'__Open_Sans_.{6}', '__Open_Sans_Fallback_.{6}'$/ - ), - fontStyle: 'normal', - }, - }) - - // with-local-fonts.js - expect(JSON.parse($('#first-local-font').text())).toEqual({ - className: expect.stringMatching(getClassNameRegex('className')), - style: { - fontFamily: expect.stringMatching( - /^'__myFont1_.{6}', '__myFont1_Fallback_.{6}', system-ui$/ - ), - fontStyle: 'italic', - fontWeight: 100, - }, - }) - expect(JSON.parse($('#second-local-font').text())).toEqual({ - className: expect.stringMatching(getClassNameRegex('className')), - variable: expect.stringMatching(getClassNameRegex('variable')), - style: { - fontFamily: expect.stringMatching( - /^'__myFont2_.{6}', '__myFont2_Fallback_.{6}'$/ - ), - }, - }) - }) - - test('Variable font without weight range', async () => { - const html = await renderViaHTTP( - next.url, - '/variable-font-without-weight-range' - ) - const $ = cheerio.load(html) - - expect(JSON.parse($('#nabla').text())).toEqual({ - className: expect.stringMatching(getClassNameRegex('className')), - style: { - fontFamily: expect.stringMatching( - /^'__Nabla_.{6}', '__Nabla_Fallback_.{6}'$/ - ), - fontStyle: 'normal', - }, - }) - }) - }) - - describe('computed styles', () => { - test('page with fonts', async () => { - const browser = await webdriver(next.url, '/with-fonts') - - // _app.js - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#app-open-sans")).fontFamily' - ) - ).toMatch(/^__Open_Sans_.{6}, __Open_Sans_Fallback_.{6}$/) - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#app-open-sans")).fontWeight' - ) - ).toBe('400') - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#app-open-sans")).fontStyle' - ) - ).toBe('normal') - - // with-fonts.js - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#with-fonts-open-sans")).fontFamily' - ) - ).toMatch(/^__Open_Sans_.{6}, __Open_Sans_Fallback_.{6}$/) - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#with-fonts-open-sans")).fontWeight' - ) - ).toBe('400') - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#with-fonts-open-sans")).fontStyle' - ) - ).toBe('normal') - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#with-fonts-open-sans-style")).fontWeight' - ) - ).toBe('400') - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#with-fonts-open-sans-style")).fontStyle' + afterAll(() => next.destroy()) + + if ((global as any).isNextDev) { + it('should use production cache control for fonts', async () => { + const html = await next.render('/') + console.log({ html }) + const $ = await next.render$('/') + const link = $('[rel="preload"][as="font"]').attr('href') + expect(link).toBeDefined() + const res = await next.fetch(link) + expect(res.headers.get('cache-control')).toBe( + 'public, max-age=31536000, immutable' ) - ).toBe('normal') - - // CompWithFonts.js - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#comp-with-fonts-inter")).fontFamily' - ) - ).toMatch(/^__Inter_.{6}, __Inter_Fallback_.{6}$/) - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#comp-with-fonts-inter")).fontWeight' - ) - ).toBe('900') - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#comp-with-fonts-inter")).fontStyle' - ) - ).toBe('normal') - - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#comp-with-fonts-roboto")).fontFamily' - ) - ).toMatch(/^__Roboto_.{6}, __Roboto_Fallback_.{6}$/) - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#comp-with-fonts-roboto")).fontWeight' - ) - ).toBe('100') - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#comp-with-fonts-roboto")).fontStyle' - ) - ).toBe('italic') - }) - - test('page using variables', async () => { - const browser = await webdriver(next.url, '/variables') - - // Fira Code Variable - const firaCodeRegex = /^__Fira_Code_.{6}, __Fira_Code_Fallback_.{6}$/ - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#variables-fira-code")).fontFamily' - ) - ).toMatch(firaCodeRegex) - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#without-variables-fira-code")).fontFamily' - ) - ).not.toMatch(firaCodeRegex) - - // Roboto 100 Italic - const roboto100ItalicRegex = /^__Roboto_.{6}, __Roboto_Fallback_.{6}$/ - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#variables-roboto-100-italic")).fontFamily' - ) - ).toMatch(roboto100ItalicRegex) - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#without-variables-roboto-100-italic")).fontFamily' - ) - ).not.toMatch(roboto100ItalicRegex) - - // Local font - const localFontRegex = /^__myFont_.{6}, __myFont_Fallback_.{6}$/ - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#variables-local-font")).fontFamily' - ) - ).toMatch(localFontRegex) - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#without-variables-local-font")).fontFamily' - ) - ).not.toMatch(localFontRegex) - }) - - test('page using fallback fonts', async () => { - const browser = await webdriver(next.url, '/with-fallback') - - // .className - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#with-fallback-fonts-classname")).fontFamily' - ) - ).toMatch(/^__Open_Sans_.{6}, system-ui, Arial$/) - - // .style - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#with-fallback-fonts-style")).fontFamily' - ) - ).toMatch(/^__Open_Sans_.{6}, system-ui, Arial$/) - - // .variable - expect( - await browser.eval( - 'getComputedStyle(document.querySelector("#with-fallback-fonts-variable")).fontFamily' - ) - ).toMatch(/^__Open_Sans_.{6}, system-ui, Arial$/) - }) - }) - - describe('preload', () => { - test('page with fonts', async () => { - const html = await renderViaHTTP(next.url, '/with-fonts') - const $ = cheerio.load(html) - - // Preconnect - expect($('link[rel="preconnect"]').length).toBe(0) - - expect($('link[as="font"]').length).toBe(2) - const links = Array.from($('link[as="font"]')).sort((a, b) => { - return a.attribs.href.localeCompare(b.attribs.href) }) - // From /_app - expect(links[0].attribs).toEqual({ - as: 'font', - crossorigin: 'anonymous', - href: '/_next/static/media/0812efcfaefec5ea-s.p.woff2', - rel: 'preload', - type: 'font/woff2', - 'data-next-font': 'size-adjust', + } + + describe('import values', () => { + test('page with font', async () => { + const html = await renderViaHTTP(next.url, '/with-fonts') + const $ = cheerio.load(html) + + // _app.js + expect(JSON.parse($('#app-open-sans').text())).toEqual({ + className: expect.stringMatching(getClassNameRegex('className')), + variable: expect.stringMatching(getClassNameRegex('variable')), + style: { + fontFamily: expect.stringMatching( + /^'__Open_Sans_.{6}', '__Open_Sans_Fallback_.{6}'$/ + ), + fontStyle: 'normal', + }, + }) + + // with-fonts.js + expect(JSON.parse($('#with-fonts-open-sans').text())).toEqual({ + className: expect.stringMatching(getClassNameRegex('className')), + variable: expect.stringMatching(getClassNameRegex('variable')), + style: { + fontFamily: expect.stringMatching( + /^'__Open_Sans_.{6}', '__Open_Sans_Fallback_.{6}'$/ + ), + fontStyle: 'normal', + }, + }) + + // CompWithFonts.js + expect(JSON.parse($('#comp-with-fonts-inter').text())).toEqual({ + className: expect.stringMatching(getClassNameRegex('className')), + style: { + fontFamily: expect.stringMatching( + /^'__Inter_.{6}', '__Inter_Fallback_.{6}'$/ + ), + fontWeight: 900, + fontStyle: 'normal', + }, + }) + expect(JSON.parse($('#comp-with-fonts-roboto').text())).toEqual({ + className: expect.stringMatching(getClassNameRegex('className')), + style: { + fontFamily: expect.stringMatching( + /^'__Roboto_.{6}', '__Roboto_Fallback_.{6}'$/ + ), + fontStyle: 'italic', + fontWeight: 100, + }, + }) }) - expect(links[1].attribs).toEqual({ - as: 'font', - crossorigin: 'anonymous', - href: '/_next/static/media/675c25f648fd6a30-s.p.woff2', - rel: 'preload', - type: 'font/woff2', - 'data-next-font': 'size-adjust', - }) - }) - test('page without fonts', async () => { - const html = await renderViaHTTP(next.url, '/without-fonts') - const $ = cheerio.load(html) - - // Preconnect - expect($('link[rel="preconnect"]').length).toBe(0) - - // From _app - expect($('link[as="font"]').length).toBe(1) - expect($('link[as="font"]').get(0).attribs).toEqual({ - as: 'font', - crossorigin: 'anonymous', - href: '/_next/static/media/0812efcfaefec5ea-s.p.woff2', - rel: 'preload', - type: 'font/woff2', - 'data-next-font': 'size-adjust', + test('page with local fonts', async () => { + const html = await renderViaHTTP(next.url, '/with-local-fonts') + const $ = cheerio.load(html) + + // _app.js + expect(JSON.parse($('#app-open-sans').text())).toEqual({ + className: expect.stringMatching(getClassNameRegex('className')), + variable: expect.stringMatching(getClassNameRegex('variable')), + style: { + fontFamily: expect.stringMatching( + /^'__Open_Sans_.{6}', '__Open_Sans_Fallback_.{6}'$/ + ), + fontStyle: 'normal', + }, + }) + + // with-local-fonts.js + expect(JSON.parse($('#first-local-font').text())).toEqual({ + className: expect.stringMatching(getClassNameRegex('className')), + style: { + fontFamily: expect.stringMatching( + /^'__myFont1_.{6}', '__myFont1_Fallback_.{6}', system-ui$/ + ), + fontStyle: 'italic', + fontWeight: 100, + }, + }) + expect(JSON.parse($('#second-local-font').text())).toEqual({ + className: expect.stringMatching(getClassNameRegex('className')), + variable: expect.stringMatching(getClassNameRegex('variable')), + style: { + fontFamily: expect.stringMatching( + /^'__myFont2_.{6}', '__myFont2_Fallback_.{6}'$/ + ), + }, + }) }) - }) - - test('page with local fonts', async () => { - const html = await renderViaHTTP(next.url, '/with-local-fonts') - const $ = cheerio.load(html) - - // Preconnect - expect($('link[rel="preconnect"]').length).toBe(0) - - // Preload - expect($('link[as="font"]').length).toBe(5) - expect( - Array.from($('link[as="font"]')) - .map((el) => el.attribs.href) - .sort() - ).toEqual([ - '/_next/static/media/02205c9944024f15-s.p.woff2', - '/_next/static/media/0812efcfaefec5ea-s.p.woff2', - '/_next/static/media/1deec1af325840fd-s.p.woff2', - '/_next/static/media/ab6fdae82d1a8d92-s.p.woff2', - '/_next/static/media/d55edb6f37902ebf-s.p.woff2', - ]) - }) - test('google fonts with multiple weights/styles', async () => { - const html = await renderViaHTTP(next.url, '/with-google-fonts') - const $ = cheerio.load(html) - - // Preconnect - expect($('link[rel="preconnect"]').length).toBe(0) - - // Preload - expect($('link[as="font"]').length).toBe(8) - - expect( - Array.from($('link[as="font"]')) - .map((el) => el.attribs.href) - .sort() - ).toEqual([ - '/_next/static/media/0812efcfaefec5ea-s.p.woff2', - '/_next/static/media/4f3dcdf40b3ca86d-s.p.woff2', - '/_next/static/media/560a6db6ac485cb1-s.p.woff2', - '/_next/static/media/686d1702f12625fe-s.p.woff2', - '/_next/static/media/86d92167ff02c708-s.p.woff2', - '/_next/static/media/9ac01b894b856187-s.p.woff2', - '/_next/static/media/c9baea324111137d-s.p.woff2', - '/_next/static/media/fb68b4558e2a718e-s.p.woff2', - ]) - }) - - test('font without preloadable subsets', async () => { - const html = await renderViaHTTP( - next.url, - '/font-without-preloadable-subsets' - ) - const $ = cheerio.load(html) - - // Preconnect - expect($('link[rel="preconnect"]').length).toBe(0) - - // From _app - expect($('link[as="font"]').length).toBe(1) - expect($('link[as="font"]').get(0).attribs).toEqual({ - as: 'font', - crossorigin: 'anonymous', - href: '/_next/static/media/0812efcfaefec5ea-s.p.woff2', - rel: 'preload', - type: 'font/woff2', - 'data-next-font': 'size-adjust', + test('Variable font without weight range', async () => { + const html = await renderViaHTTP( + next.url, + '/variable-font-without-weight-range' + ) + const $ = cheerio.load(html) + + expect(JSON.parse($('#nabla').text())).toEqual({ + className: expect.stringMatching(getClassNameRegex('className')), + style: { + fontFamily: expect.stringMatching( + /^'__Nabla_.{6}', '__Nabla_Fallback_.{6}'$/ + ), + fontStyle: 'normal', + }, + }) }) }) - test('font without size adjust', async () => { - const html = await renderViaHTTP(next.url, '/with-fallback') - const $ = cheerio.load(html) - const links = Array.from($('link[as="font"]')).sort((a, b) => { - return a.attribs.href.localeCompare(b.attribs.href) + describe('computed styles', () => { + test('page with fonts', async () => { + const browser = await webdriver(next.url, '/with-fonts') + + // _app.js + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#app-open-sans")).fontFamily' + ) + ).toMatch(/^__Open_Sans_.{6}, __Open_Sans_Fallback_.{6}$/) + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#app-open-sans")).fontWeight' + ) + ).toBe('400') + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#app-open-sans")).fontStyle' + ) + ).toBe('normal') + + // with-fonts.js + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#with-fonts-open-sans")).fontFamily' + ) + ).toMatch(/^__Open_Sans_.{6}, __Open_Sans_Fallback_.{6}$/) + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#with-fonts-open-sans")).fontWeight' + ) + ).toBe('400') + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#with-fonts-open-sans")).fontStyle' + ) + ).toBe('normal') + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#with-fonts-open-sans-style")).fontWeight' + ) + ).toBe('400') + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#with-fonts-open-sans-style")).fontStyle' + ) + ).toBe('normal') + + // CompWithFonts.js + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#comp-with-fonts-inter")).fontFamily' + ) + ).toMatch(/^__Inter_.{6}, __Inter_Fallback_.{6}$/) + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#comp-with-fonts-inter")).fontWeight' + ) + ).toBe('900') + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#comp-with-fonts-inter")).fontStyle' + ) + ).toBe('normal') + + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#comp-with-fonts-roboto")).fontFamily' + ) + ).toMatch(/^__Roboto_.{6}, __Roboto_Fallback_.{6}$/) + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#comp-with-fonts-roboto")).fontWeight' + ) + ).toBe('100') + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#comp-with-fonts-roboto")).fontStyle' + ) + ).toBe('italic') }) - expect(links[1].attribs).toEqual({ - as: 'font', - crossorigin: 'anonymous', - href: '/_next/static/media/0812efcfaefec5ea.p.woff2', - rel: 'preload', - type: 'font/woff2', - 'data-next-font': '', + + test('page using variables', async () => { + const browser = await webdriver(next.url, '/variables') + + // Fira Code Variable + const firaCodeRegex = /^__Fira_Code_.{6}, __Fira_Code_Fallback_.{6}$/ + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#variables-fira-code")).fontFamily' + ) + ).toMatch(firaCodeRegex) + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#without-variables-fira-code")).fontFamily' + ) + ).not.toMatch(firaCodeRegex) + + // Roboto 100 Italic + const roboto100ItalicRegex = /^__Roboto_.{6}, __Roboto_Fallback_.{6}$/ + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#variables-roboto-100-italic")).fontFamily' + ) + ).toMatch(roboto100ItalicRegex) + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#without-variables-roboto-100-italic")).fontFamily' + ) + ).not.toMatch(roboto100ItalicRegex) + + // Local font + const localFontRegex = /^__myFont_.{6}, __myFont_Fallback_.{6}$/ + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#variables-local-font")).fontFamily' + ) + ).toMatch(localFontRegex) + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#without-variables-local-font")).fontFamily' + ) + ).not.toMatch(localFontRegex) }) - expect(links[2].attribs).toEqual({ - as: 'font', - crossorigin: 'anonymous', - href: '/_next/static/media/ab6fdae82d1a8d92.p.woff2', - rel: 'preload', - type: 'font/woff2', - 'data-next-font': '', + test('page using fallback fonts', async () => { + const browser = await webdriver(next.url, '/with-fallback') + + // .className + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#with-fallback-fonts-classname")).fontFamily' + ) + ).toMatch(/^__Open_Sans_.{6}, system-ui, Arial$/) + + // .style + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#with-fallback-fonts-style")).fontFamily' + ) + ).toMatch(/^__Open_Sans_.{6}, system-ui, Arial$/) + + // .variable + expect( + await browser.eval( + 'getComputedStyle(document.querySelector("#with-fallback-fonts-variable")).fontFamily' + ) + ).toMatch(/^__Open_Sans_.{6}, system-ui, Arial$/) }) }) - }) - describe('Fallback fontfaces', () => { - describe('local', () => { - test('Indie flower', async () => { - const browser = await webdriver(next.url, '/with-local-fonts') - - const ascentOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("myFont2_Fallback")).ascentOverride' - ) - expect(ascentOverride).toBe('103.26%') - - const descentOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("myFont2_Fallback")).descentOverride' - ) - expect(descentOverride).toBe('51.94%') - - const lineGapOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("myFont2_Fallback")).lineGapOverride' - ) - expect(lineGapOverride).toBe('0%') - - const sizeAdjust = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("myFont2_Fallback")).sizeAdjust' - ) - expect(sizeAdjust).toBe('94%') + describe('preload', () => { + test('page with fonts', async () => { + const html = await renderViaHTTP(next.url, '/with-fonts') + const $ = cheerio.load(html) + + // Preconnect + expect($('link[rel="preconnect"]').length).toBe(0) + + expect($('link[as="font"]').length).toBe(2) + const links = Array.from($('link[as="font"]')).sort((a, b) => { + return a.attribs.href.localeCompare(b.attribs.href) + }) + // From /_app + expect(links[0].attribs).toEqual({ + as: 'font', + crossorigin: 'anonymous', + href: '/_next/static/media/0812efcfaefec5ea-s.p.woff2', + rel: 'preload', + type: 'font/woff2', + 'data-next-font': 'size-adjust', + }) + expect(links[1].attribs).toEqual({ + as: 'font', + crossorigin: 'anonymous', + href: '/_next/static/media/675c25f648fd6a30-s.p.woff2', + rel: 'preload', + type: 'font/woff2', + 'data-next-font': 'size-adjust', + }) }) - test('Fraunces', async () => { - const browser = await webdriver(next.url, '/with-local-fonts') - - const ascentOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("myFont1_Fallback")).ascentOverride' - ) - expect(ascentOverride).toBe('84.71%') - - const descentOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("myFont1_Fallback")).descentOverride' - ) - expect(descentOverride).toBe('22.09%') - - const lineGapOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("myFont1_Fallback")).lineGapOverride' - ) - expect(lineGapOverride).toBe('0%') - - const sizeAdjust = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("myFont1_Fallback")).sizeAdjust' - ) - expect(sizeAdjust).toBe('115.45%') + test('page without fonts', async () => { + const html = await renderViaHTTP(next.url, '/without-fonts') + const $ = cheerio.load(html) + + // Preconnect + expect($('link[rel="preconnect"]').length).toBe(0) + + // From _app + expect($('link[as="font"]').length).toBe(1) + expect($('link[as="font"]').get(0).attribs).toEqual({ + as: 'font', + crossorigin: 'anonymous', + href: '/_next/static/media/0812efcfaefec5ea-s.p.woff2', + rel: 'preload', + type: 'font/woff2', + 'data-next-font': 'size-adjust', + }) }) - test('Roboto multiple weights and styles', async () => { - const browser = await webdriver(next.url, '/with-local-fonts') - - const ascentOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("roboto_Fallback")).ascentOverride' - ) - expect(ascentOverride).toBe('92.49%') - - const descentOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("roboto_Fallback")).descentOverride' - ) - expect(descentOverride).toBe('24.34%') - - const lineGapOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("roboto_Fallback")).lineGapOverride' - ) - expect(lineGapOverride).toBe('0%') - - const sizeAdjust = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("roboto_Fallback")).sizeAdjust' - ) - expect(sizeAdjust).toBe('100.3%') + test('page with local fonts', async () => { + const html = await renderViaHTTP(next.url, '/with-local-fonts') + const $ = cheerio.load(html) + + // Preconnect + expect($('link[rel="preconnect"]').length).toBe(0) + + // Preload + expect($('link[as="font"]').length).toBe(5) + expect( + Array.from($('link[as="font"]')) + .map((el) => el.attribs.href) + .sort() + ).toEqual([ + '/_next/static/media/02205c9944024f15-s.p.woff2', + '/_next/static/media/0812efcfaefec5ea-s.p.woff2', + '/_next/static/media/1deec1af325840fd-s.p.woff2', + '/_next/static/media/ab6fdae82d1a8d92-s.p.woff2', + '/_next/static/media/d55edb6f37902ebf-s.p.woff2', + ]) }) - test('Roboto multiple weights and styles - variable 1', async () => { - const browser = await webdriver(next.url, '/with-local-fonts') - - const ascentOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("robotoVar1_Fallback")).ascentOverride' - ) - expect(ascentOverride).toBe('92.49%') - - const descentOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("robotoVar1_Fallback")).descentOverride' - ) - expect(descentOverride).toBe('24.34%') - - const lineGapOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("robotoVar1_Fallback")).lineGapOverride' - ) - expect(lineGapOverride).toBe('0%') - - const sizeAdjust = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("robotoVar1_Fallback")).sizeAdjust' - ) - expect(sizeAdjust).toBe('100.3%') + test('google fonts with multiple weights/styles', async () => { + const html = await renderViaHTTP(next.url, '/with-google-fonts') + const $ = cheerio.load(html) + + // Preconnect + expect($('link[rel="preconnect"]').length).toBe(0) + + // Preload + expect($('link[as="font"]').length).toBe(8) + + expect( + Array.from($('link[as="font"]')) + .map((el) => el.attribs.href) + .sort() + ).toEqual([ + '/_next/static/media/0812efcfaefec5ea-s.p.woff2', + '/_next/static/media/4f3dcdf40b3ca86d-s.p.woff2', + '/_next/static/media/560a6db6ac485cb1-s.p.woff2', + '/_next/static/media/686d1702f12625fe-s.p.woff2', + '/_next/static/media/86d92167ff02c708-s.p.woff2', + '/_next/static/media/9ac01b894b856187-s.p.woff2', + '/_next/static/media/c9baea324111137d-s.p.woff2', + '/_next/static/media/fb68b4558e2a718e-s.p.woff2', + ]) }) - test('Roboto multiple weights and styles - variable 2', async () => { - const browser = await webdriver(next.url, '/with-local-fonts') - - const ascentOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("robotoVar2_Fallback")).ascentOverride' - ) - expect(ascentOverride).toBe('92.49%') - - const descentOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("robotoVar2_Fallback")).descentOverride' - ) - expect(descentOverride).toBe('24.34%') - - const lineGapOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("robotoVar2_Fallback")).lineGapOverride' - ) - expect(lineGapOverride).toBe('0%') + test('font without preloadable subsets', async () => { + const html = await renderViaHTTP( + next.url, + '/font-without-preloadable-subsets' + ) + const $ = cheerio.load(html) + + // Preconnect + expect($('link[rel="preconnect"]').length).toBe(0) + + // From _app + expect($('link[as="font"]').length).toBe(1) + expect($('link[as="font"]').get(0).attribs).toEqual({ + as: 'font', + crossorigin: 'anonymous', + href: '/_next/static/media/0812efcfaefec5ea-s.p.woff2', + rel: 'preload', + type: 'font/woff2', + 'data-next-font': 'size-adjust', + }) + }) - const sizeAdjust = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("robotoVar2_Fallback")).sizeAdjust' - ) - expect(sizeAdjust).toBe('100.3%') + test('font without size adjust', async () => { + const html = await renderViaHTTP(next.url, '/with-fallback') + const $ = cheerio.load(html) + const links = Array.from($('link[as="font"]')).sort((a, b) => { + return a.attribs.href.localeCompare(b.attribs.href) + }) + expect(links[1].attribs).toEqual({ + as: 'font', + crossorigin: 'anonymous', + href: '/_next/static/media/0812efcfaefec5ea.p.woff2', + rel: 'preload', + type: 'font/woff2', + 'data-next-font': '', + }) + + expect(links[2].attribs).toEqual({ + as: 'font', + crossorigin: 'anonymous', + href: '/_next/static/media/ab6fdae82d1a8d92.p.woff2', + rel: 'preload', + type: 'font/woff2', + 'data-next-font': '', + }) }) }) - describe('google', () => { - test('Indie flower', async () => { - const browser = await webdriver(next.url, '/with-google-fonts') - - const ascentOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("Indie_Flower_Fallback")).ascentOverride' - ) - expect(ascentOverride).toBe('101.1%') - - const descentOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("Indie_Flower_Fallback")).descentOverride' - ) - expect(descentOverride).toBe('50.85%') - - const lineGapOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("Indie_Flower_Fallback")).lineGapOverride' - ) - expect(lineGapOverride).toBe('0%') - - const sizeAdjust = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("Indie_Flower_Fallback")).sizeAdjust' - ) - expect(sizeAdjust).toBe('96.02%') + describe('Fallback fontfaces', () => { + describe('local', () => { + test('Indie flower', async () => { + const browser = await webdriver(next.url, '/with-local-fonts') + + const ascentOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("myFont2_Fallback")).ascentOverride' + ) + expect(ascentOverride).toBe('103.26%') + + const descentOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("myFont2_Fallback")).descentOverride' + ) + expect(descentOverride).toBe('51.94%') + + const lineGapOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("myFont2_Fallback")).lineGapOverride' + ) + expect(lineGapOverride).toBe('0%') + + const sizeAdjust = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("myFont2_Fallback")).sizeAdjust' + ) + expect(sizeAdjust).toBe('94%') + }) + + test('Fraunces', async () => { + const browser = await webdriver(next.url, '/with-local-fonts') + + const ascentOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("myFont1_Fallback")).ascentOverride' + ) + expect(ascentOverride).toBe('84.71%') + + const descentOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("myFont1_Fallback")).descentOverride' + ) + expect(descentOverride).toBe('22.09%') + + const lineGapOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("myFont1_Fallback")).lineGapOverride' + ) + expect(lineGapOverride).toBe('0%') + + const sizeAdjust = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("myFont1_Fallback")).sizeAdjust' + ) + expect(sizeAdjust).toBe('115.45%') + }) + + test('Roboto multiple weights and styles', async () => { + const browser = await webdriver(next.url, '/with-local-fonts') + + const ascentOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("roboto_Fallback")).ascentOverride' + ) + expect(ascentOverride).toBe('92.49%') + + const descentOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("roboto_Fallback")).descentOverride' + ) + expect(descentOverride).toBe('24.34%') + + const lineGapOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("roboto_Fallback")).lineGapOverride' + ) + expect(lineGapOverride).toBe('0%') + + const sizeAdjust = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("roboto_Fallback")).sizeAdjust' + ) + expect(sizeAdjust).toBe('100.3%') + }) + + test('Roboto multiple weights and styles - variable 1', async () => { + const browser = await webdriver(next.url, '/with-local-fonts') + + const ascentOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("robotoVar1_Fallback")).ascentOverride' + ) + expect(ascentOverride).toBe('92.49%') + + const descentOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("robotoVar1_Fallback")).descentOverride' + ) + expect(descentOverride).toBe('24.34%') + + const lineGapOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("robotoVar1_Fallback")).lineGapOverride' + ) + expect(lineGapOverride).toBe('0%') + + const sizeAdjust = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("robotoVar1_Fallback")).sizeAdjust' + ) + expect(sizeAdjust).toBe('100.3%') + }) + + test('Roboto multiple weights and styles - variable 2', async () => { + const browser = await webdriver(next.url, '/with-local-fonts') + + const ascentOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("robotoVar2_Fallback")).ascentOverride' + ) + expect(ascentOverride).toBe('92.49%') + + const descentOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("robotoVar2_Fallback")).descentOverride' + ) + expect(descentOverride).toBe('24.34%') + + const lineGapOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("robotoVar2_Fallback")).lineGapOverride' + ) + expect(lineGapOverride).toBe('0%') + + const sizeAdjust = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("robotoVar2_Fallback")).sizeAdjust' + ) + expect(sizeAdjust).toBe('100.3%') + }) }) - test('Fraunces', async () => { - const browser = await webdriver(next.url, '/with-google-fonts') - - const ascentOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("Fraunces_Fallback")).ascentOverride' - ) - expect(ascentOverride).toBe('84.29%') - - const descentOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("Fraunces_Fallback")).descentOverride' - ) - expect(descentOverride).toBe('21.98%') - - const lineGapOverride = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("Fraunces_Fallback")).lineGapOverride' - ) - expect(lineGapOverride).toBe('0%') - - const sizeAdjust = await browser.eval( - 'Array.from(document.fonts.values()).find(font => font.family.includes("Fraunces_Fallback")).sizeAdjust' - ) - expect(sizeAdjust).toBe('116.03%') + describe('google', () => { + test('Indie flower', async () => { + const browser = await webdriver(next.url, '/with-google-fonts') + + const ascentOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("Indie_Flower_Fallback")).ascentOverride' + ) + expect(ascentOverride).toBe('101.1%') + + const descentOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("Indie_Flower_Fallback")).descentOverride' + ) + expect(descentOverride).toBe('50.85%') + + const lineGapOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("Indie_Flower_Fallback")).lineGapOverride' + ) + expect(lineGapOverride).toBe('0%') + + const sizeAdjust = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("Indie_Flower_Fallback")).sizeAdjust' + ) + expect(sizeAdjust).toBe('96.02%') + }) + + test('Fraunces', async () => { + const browser = await webdriver(next.url, '/with-google-fonts') + + const ascentOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("Fraunces_Fallback")).ascentOverride' + ) + expect(ascentOverride).toBe('84.29%') + + const descentOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("Fraunces_Fallback")).descentOverride' + ) + expect(descentOverride).toBe('21.98%') + + const lineGapOverride = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("Fraunces_Fallback")).lineGapOverride' + ) + expect(lineGapOverride).toBe('0%') + + const sizeAdjust = await browser.eval( + 'Array.from(document.fonts.values()).find(font => font.family.includes("Fraunces_Fallback")).sizeAdjust' + ) + expect(sizeAdjust).toBe('116.03%') + }) }) }) }) - }) + } })