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 926a345ad1004..69ffab5198dc4 100644 --- a/test/e2e/app-dir/next-font/next-font.test.ts +++ b/test/e2e/app-dir/next-font/next-font.test.ts @@ -23,427 +23,429 @@ const getAttrs = (elems: Cheerio) => // My machine behaves differently to CI .sort((a, b) => (a.href < b.href ? -1 : 1)) -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')), +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, }, - 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') - }) - }) - - if (!isDev) { - describe('preload', () => { - it('should preload correctly with server components', async () => { + ({ 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') - // Break file - await next.patchFile( - 'fonts/index.js', - font1Content.replace('./font1.woff2', './does-not-exist.woff2') + // Preconnect + expect($('link[rel="preconnect"]').length).toBe(0) + // Preload + expect(getAttrs($('link[as="font"]'))).toEqual([]) + }) + }) + } + + 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, true)).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' ) - // Fix file - await next.patchFile('fonts/index.js', font1Content) - await browser.waitForElementByCss('#root-page') + // 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' + ) }) }) + + 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, true)).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') + }) + }) + } } - } - ) + ) + }) })