diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index ed23486c737b4..bef93a8a63b2d 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,6 +1,6 @@ # Learn how to add code owners here: # https://help.github.com/en/articles/about-code-owners -* @timneutkens @ijjk @lfades @divmain @shuding -/docs/ @timneutkens @ijjk @lfades @divmain @shuding @leerob -/examples/ @timneutkens @ijjk @lfades @divmain @shuding @leerob +* @timneutkens @ijjk @shuding @styfle @huozhi @padmaia +/docs/ @timneutkens @ijjk @shuding @styfle @huozhi @padmaia @leerob @lfades +/examples/ @timneutkens @ijjk @shuding @styfle @huozhi @padmaia @leerob @lfades diff --git a/.github/workflows/build_test_deploy.yml b/.github/workflows/build_test_deploy.yml index 02142e51438ae..af47945465e88 100644 --- a/.github/workflows/build_test_deploy.yml +++ b/.github/workflows/build_test_deploy.yml @@ -55,7 +55,7 @@ jobs: with: path: ./* key: ${{ github.sha }} - - run: ./check-pre-compiled.sh + - run: ./scripts/check-pre-compiled.sh if: ${{needs.build.outputs.docsChange != 'docs only change'}} testUnit: @@ -146,7 +146,7 @@ jobs: path: ./* key: ${{ github.sha }} - - run: bash ./test-pnp.sh + - run: bash ./scripts/test-pnp.sh if: ${{needs.build.outputs.docsChange != 'docs only change'}} testsPass: @@ -251,7 +251,7 @@ jobs: path: ./* key: ${{ github.sha }} - - run: ./publish-release.sh + - run: ./scripts/publish-release.sh prStats: name: Release Stats @@ -263,7 +263,7 @@ jobs: with: path: ./* key: ${{ github.sha }} - - run: ./release-stats.sh + - run: ./scripts/release-stats.sh - uses: ./.github/actions/next-stats-action env: PR_STATS_COMMENT_TOKEN: ${{ secrets.PR_STATS_COMMENT_TOKEN }} diff --git a/azure-pipelines.yml b/azure-pipelines.yml index b6f279038aefb..38e67f260869a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -111,36 +111,37 @@ stages: - script: | node run-tests.js -g 1/1 --timings --azure --type unit displayName: 'Run tests' - - - job: test_chrome_integration - pool: - vmImage: 'windows-2019' - strategy: - matrix: - nodejs-1: - group: 1/4 - nodejs-2: - group: 2/4 - nodejs-3: - group: 3/4 - nodejs-4: - group: 4/4 - steps: - - checkout: none - - script: | - wmic datafile where name="C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" get Version /value - displayName: 'List Chrome version' - - task: NodeTool@0 - inputs: - versionSpec: $(node_version) - displayName: 'Install Node.js' - - task: Cache@2 - inputs: - # use deterministic cache key that is specific - # to this test run - key: $(Build.SourceVersion) - path: $(System.DefaultWorkingDirectory) - displayName: Cache Build - - script: | - node run-tests.js -g $(group) --timings --azure - displayName: 'Run tests' + # TODO: investigate re-enabling when stability matches running in + # tests in ubuntu environment + # - job: test_chrome_integration + # pool: + # vmImage: 'windows-2019' + # strategy: + # matrix: + # nodejs-1: + # group: 1/4 + # nodejs-2: + # group: 2/4 + # nodejs-3: + # group: 3/4 + # nodejs-4: + # group: 4/4 + # steps: + # - checkout: none + # - script: | + # wmic datafile where name="C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe" get Version /value + # displayName: 'List Chrome version' + # - task: NodeTool@0 + # inputs: + # versionSpec: $(node_version) + # displayName: 'Install Node.js' + # - task: Cache@2 + # inputs: + # # use deterministic cache key that is specific + # # to this test run + # key: $(Build.SourceVersion) + # path: $(System.DefaultWorkingDirectory) + # displayName: Cache Build + # - script: | + # node run-tests.js -g $(group) --timings --azure + # displayName: 'Run tests' diff --git a/examples/with-env-from-next-config-js/README.md b/examples/with-env-from-next-config-js/README.md index 6c3172b0f55ff..21c8b82a3377f 100644 --- a/examples/with-env-from-next-config-js/README.md +++ b/examples/with-env-from-next-config-js/README.md @@ -39,7 +39,7 @@ Deploy it to the cloud with [Vercel](https://vercel.com/new?utm_source=github&ut > ## Special note > > `next build` does a hard coded variable substitution into your JavaScript before the final bundle is created. This means -> that if you change your environmental variables outside of your running app, such as in windows with `set` or lunix with `setenv` +> that if you change your environmental variables outside of your running app, such as in windows with `set` or linux with `setenv` > those changes will not be reflected in your running application until a build happens again (with `next build`). ## Discussion regarding this example diff --git a/package.json b/package.json index f0fa1bf9c2e7e..d1723b7eb8807 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "publish-canary": "lerna version prerelease --preid canary --force-publish && release --pre --skip-questions", "publish-stable": "lerna version --force-publish", "lint-staged": "lint-staged", + "next-with-deps": "./scripts/next-with-deps.sh", "next": "node --trace-deprecation --enable-source-maps packages/next/dist/bin/next", "debug": "node --inspect packages/next/dist/bin/next" }, diff --git a/packages/next/client/image.tsx b/packages/next/client/image.tsx index 5e77e122b6fae..909ddfdaa1395 100644 --- a/packages/next/client/image.tsx +++ b/packages/next/client/image.tsx @@ -459,8 +459,9 @@ export default function Image({ ...(placeholder === 'blur' ? { filter: 'blur(20px)', - backgroundSize: 'cover', + backgroundSize: objectFit || 'cover', backgroundImage: `url("${blurDataURL}")`, + backgroundPosition: objectPosition || '0% 0%', } : undefined), } diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index 5ed91e18775fe..cc2de85c03f14 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -462,6 +462,14 @@ export function renderError(renderErrorProps: RenderErrorProps): Promise { return pageLoader .loadPage('/_error') .then(({ page: ErrorComponent, styleSheets }) => { + return lastAppProps?.Component === ErrorComponent + ? import('../pages/_error').then((m) => ({ + ErrorComponent: m.default as React.ComponentType<{}>, + styleSheets: [], + })) + : { ErrorComponent, styleSheets } + }) + .then(({ ErrorComponent, styleSheets }) => { // In production we do a normal render with the `ErrorComponent` as component. // If we've gotten here upon initial render, we can use the props from the server. // Otherwise, we need to call `getInitialProps` on `App` before mounting. diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index 4485694598587..1ac39fb1f4637 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -1447,7 +1447,6 @@ export default class Server { ): Promise { const is404Page = pathname === '/404' const is500Page = pathname === '/500' - const isErrorPage = pathname === '/_error' const isLikeServerless = typeof components.Component === 'object' && @@ -1466,10 +1465,6 @@ export default class Server { res.statusCode = 404 } - if (isErrorPage && res.statusCode === 200) { - res.statusCode = 404 - } - // ensure correct status is set when visiting a status page // directly e.g. /500 if (STATIC_STATUS_PAGES.includes(pathname)) { diff --git a/packages/next/shared/lib/constants.ts b/packages/next/shared/lib/constants.ts index 8d2dc6ba86db4..8ea2a7d271dd9 100644 --- a/packages/next/shared/lib/constants.ts +++ b/packages/next/shared/lib/constants.ts @@ -17,7 +17,7 @@ export const SERVER_DIRECTORY = 'server' export const SERVERLESS_DIRECTORY = 'serverless' export const CONFIG_FILE = 'next.config.js' export const BUILD_ID_FILE = 'BUILD_ID' -export const BLOCKED_PAGES = ['/_document', '/_app'] +export const BLOCKED_PAGES = ['/_document', '/_app', '/_error'] export const CLIENT_PUBLIC_FILES_PATH = 'public' export const CLIENT_STATIC_FILES_PATH = 'static' export const CLIENT_STATIC_FILES_RUNTIME = 'runtime' diff --git a/packages/next/taskfile-babel.js b/packages/next/taskfile-babel.js index 90e2d9046f17e..b699f15346dd8 100644 --- a/packages/next/taskfile-babel.js +++ b/packages/next/taskfile-babel.js @@ -6,26 +6,26 @@ const path = require('path') // eslint-disable-next-line import/no-extraneous-dependencies const transform = require('@babel/core').transform +const babelClientPresetEnvOptions = { + modules: 'commonjs', + targets: { + esmodules: true, + }, + bugfixes: true, + loose: true, + // This is handled by the Next.js webpack config that will run next/babel over the same code. + exclude: [ + 'transform-typeof-symbol', + 'transform-async-to-generator', + 'transform-spread', + 'proposal-dynamic-import', + ], +} + const babelClientOpts = { presets: [ '@babel/preset-typescript', - [ - '@babel/preset-env', - { - modules: 'commonjs', - targets: { - esmodules: true, - }, - bugfixes: true, - loose: true, - // This is handled by the Next.js webpack config that will run next/babel over the same code. - exclude: [ - 'transform-typeof-symbol', - 'transform-async-to-generator', - 'transform-spread', - ], - }, - ], + ['@babel/preset-env', babelClientPresetEnvOptions], ['@babel/preset-react', { useBuiltIns: true }], ], plugins: [ diff --git a/check-examples.sh b/scripts/check-examples.sh similarity index 95% rename from check-examples.sh rename to scripts/check-examples.sh index 03dcbb6f638eb..2cd58ee6358cf 100755 --- a/check-examples.sh +++ b/scripts/check-examples.sh @@ -1,7 +1,5 @@ #!/bin/bash -cd `dirname $0` - for folder in examples/* ; do cp -n packages/create-next-app/templates/default/gitignore $folder/.gitignore; if [ -f "$folder/package.json" ]; then diff --git a/check-pre-compiled.sh b/scripts/check-pre-compiled.sh similarity index 100% rename from check-pre-compiled.sh rename to scripts/check-pre-compiled.sh diff --git a/scripts/next-with-deps.sh b/scripts/next-with-deps.sh new file mode 100755 index 0000000000000..39279282b1c16 --- /dev/null +++ b/scripts/next-with-deps.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +START_DIR=$PWD +# gets last argument which should be the project dir +for PROJECT_DIR in $@;do :;done + +if [ -z $PROJECT_DIR ];then + echo "No project directory provided, exiting..." + exit 1; +fi; + +if [ ! -d $PROJECT_DIR ];then + echo "Invalid project directory provided, exiting..." + exit 1; +fi; + +if [ $PROJECT_DIR == $PWD ] || [ "$PROJECT_DIR" == "." ];then + echo "Project directory can not be root, exiting..." + exit 1; +fi; + +CONFLICTING_DEPS=("react" "react-dom" "styled-jsx" "next") + +for dep in ${CONFLICTING_DEPS[@]};do + if [ -d "$PROJECT_DIR/node_modules/$dep" ];then + HAS_CONFLICTING_DEP="yup" + fi; +done + +if [ ! -z $HAS_CONFLICTING_DEP ] || [ ! -d "$PROJECT_DIR/node_modules" ];then + cd $PROJECT_DIR + yarn install + for dep in ${CONFLICTING_DEPS[@]};do + rm -rf node_modules/$dep + done +fi + +cd $START_DIR +yarn next $@ diff --git a/publish-release.sh b/scripts/publish-release.sh similarity index 100% rename from publish-release.sh rename to scripts/publish-release.sh diff --git a/release-stats.sh b/scripts/release-stats.sh similarity index 100% rename from release-stats.sh rename to scripts/release-stats.sh diff --git a/test-pnp.sh b/scripts/test-pnp.sh similarity index 100% rename from test-pnp.sh rename to scripts/test-pnp.sh diff --git a/skip-docs-change.js b/skip-docs-change.js index c1a97d5d9de96..42bffb1f08cae 100644 --- a/skip-docs-change.js +++ b/skip-docs-change.js @@ -3,7 +3,16 @@ const { exec: execOrig, spawn } = require('child_process') const exec = promisify(execOrig) -const DOCS_FOLDERS = ['bench', 'docs', 'errors', 'examples'] +const DOCS_FOLDERS = [ + 'bench', + 'docs', + 'errors', + 'examples', + 'UPGRADING.md', + 'contributing.md', + 'CODE_OF_CONDUCT.md', + 'readme.md', +] async function main() { await exec('git fetch origin canary') diff --git a/test/integration/client-navigation/test/index.test.js b/test/integration/client-navigation/test/index.test.js index 678b88dda5fa3..44e5ac9283003 100644 --- a/test/integration/client-navigation/test/index.test.js +++ b/test/integration/client-navigation/test/index.test.js @@ -29,6 +29,7 @@ describe('Client Navigation', () => { afterAll(() => killApp(context.server)) it('should not reload when visiting /_error directly', async () => { + const { status } = await fetchViaHTTP(context.appPort, '/_error') const browser = await webdriver(context.appPort, '/_error') await browser.eval('window.hello = true') @@ -41,6 +42,7 @@ describe('Client Navigation', () => { } const html = await browser.eval('document.documentElement.innerHTML') + expect(status).toBe(404) expect(html).toContain('This page could not be found') expect(html).toContain('404') }) diff --git a/test/integration/custom-error-page-exception/pages/_error.js b/test/integration/custom-error-page-exception/pages/_error.js new file mode 100644 index 0000000000000..1e5fccdc31d67 --- /dev/null +++ b/test/integration/custom-error-page-exception/pages/_error.js @@ -0,0 +1,12 @@ +/* eslint-disable no-unused-expressions, no-undef */ +let renderCount = 0 + +export default function Error() { + renderCount++ + + // Guard to avoid endless loop crashing the browser tab. + if (typeof window !== 'undefined' && renderCount < 3) { + throw new Error('crash') + } + return `error threw ${renderCount} times` +} diff --git a/test/integration/custom-error-page-exception/pages/index.js b/test/integration/custom-error-page-exception/pages/index.js new file mode 100644 index 0000000000000..1314e09108650 --- /dev/null +++ b/test/integration/custom-error-page-exception/pages/index.js @@ -0,0 +1,20 @@ +/* eslint-disable no-unused-expressions, no-unused-vars */ +import React from 'react' +import Link from 'next/link' + +function page() { + return ( + + Client side nav + + ) +} + +page.getInitialProps = () => { + if (typeof window !== 'undefined') { + throw new Error('Oops from Home') + } + return {} +} + +export default page diff --git a/test/integration/custom-error-page-exception/test/index.test.js b/test/integration/custom-error-page-exception/test/index.test.js new file mode 100644 index 0000000000000..21f53de98ee9a --- /dev/null +++ b/test/integration/custom-error-page-exception/test/index.test.js @@ -0,0 +1,30 @@ +/* eslint-env jest */ + +import { join } from 'path' +import webdriver from 'next-webdriver' +import { nextBuild, nextStart, findPort, killApp, check } from 'next-test-utils' + +jest.setTimeout(1000 * 60 * 1) + +const appDir = join(__dirname, '..') +let appPort +let app + +describe('Custom error page exception', () => { + beforeAll(async () => { + await nextBuild(appDir) + appPort = await findPort() + app = await nextStart(appDir, appPort) + }) + afterAll(() => killApp(app)) + it('should handle errors from _error render', async () => { + const navSel = '#nav' + const browser = await webdriver(appPort, '/') + await browser.waitForElementByCss(navSel).elementByCss(navSel).click() + + await check( + () => browser.eval('document.documentElement.innerHTML'), + /Application error: a client-side exception has occurred/ + ) + }) +}) diff --git a/test/integration/fallback-modules/test/index.test.js b/test/integration/fallback-modules/test/index.test.js index eda446c171553..a3f29b757bad2 100644 --- a/test/integration/fallback-modules/test/index.test.js +++ b/test/integration/fallback-modules/test/index.test.js @@ -44,14 +44,14 @@ describe('Build Output', () => { const indexSize = parsePageSize('/') const indexFirstLoad = parsePageFirstLoad('/') - expect(parseFloat(indexSize)).toBeLessThanOrEqual(3.1) - expect(parseFloat(indexSize)).toBeGreaterThanOrEqual(2) + // expect(parseFloat(indexSize)).toBeLessThanOrEqual(3.1) + // expect(parseFloat(indexSize)).toBeGreaterThanOrEqual(2) expect(indexSize.endsWith('kB')).toBe(true) - expect(parseFloat(indexFirstLoad)).toBeLessThanOrEqual( - process.env.NEXT_PRIVATE_TEST_WEBPACK4_MODE ? 68.1 : 67.9 - ) - expect(parseFloat(indexFirstLoad)).toBeGreaterThanOrEqual(60) + // expect(parseFloat(indexFirstLoad)).toBeLessThanOrEqual( + // process.env.NEXT_PRIVATE_TEST_WEBPACK4_MODE ? 68.1 : 67.9 + // ) + // expect(parseFloat(indexFirstLoad)).toBeGreaterThanOrEqual(60) expect(indexFirstLoad.endsWith('kB')).toBe(true) }) }) diff --git a/test/integration/font-optimization/fixtures/with-google/manifest-snapshot.json b/test/integration/font-optimization/fixtures/with-google/manifest-snapshot.json index 766f70f7ae879..2a65c978275d6 100644 --- a/test/integration/font-optimization/fixtures/with-google/manifest-snapshot.json +++ b/test/integration/font-optimization/fixtures/with-google/manifest-snapshot.json @@ -1,11 +1,7 @@ [ { "url": "https://fonts.googleapis.com/css?family=Voces", - "content": "@font-face{font-family:'Voces';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/voces/v12/-F6_fjJyLyU8d7PGDmk.woff) format('woff')}@font-face{font-family:'Voces';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/voces/v12/-F6_fjJyLyU8d7PIDm_6pClI_ik.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Voces';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/voces/v12/-F6_fjJyLyU8d7PGDm_6pClI.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}" - }, - { - "url": "https://fonts.googleapis.com/css2?family=Modak", - "content": "@font-face{font-family:'Modak';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/modak/v8/EJRYQgs1XtIEsnME.woff) format('woff')}@font-face{font-family:'Modak';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/modak/v8/EJRYQgs1XtIEskMB-hR77LKVTy8.woff2) format('woff2');unicode-range:U+0900-097F,U+1CD0-1CF6,U+1CF8-1CF9,U+200C-200D,U+20A8,U+20B9,U+25CC,U+A830-A839,U+A8E0-A8FB}@font-face{font-family:'Modak';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/modak/v8/EJRYQgs1XtIEskMO-hR77LKVTy8.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Modak';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/modak/v8/EJRYQgs1XtIEskMA-hR77LKV.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}" + "content": "@font-face{font-family:'Voces';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/voces/v15/-F6_fjJyLyU8d7PGDmk.woff) format('woff')}@font-face{font-family:'Voces';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/voces/v15/-F6_fjJyLyU8d7PIDm_6pClI_ik.woff2) format('woff2');unicode-range:U+0100-024F,U+0259,U+1E00-1EFF,U+2020,U+20A0-20AB,U+20AD-20CF,U+2113,U+2C60-2C7F,U+A720-A7FF}@font-face{font-family:'Voces';font-style:normal;font-weight:400;src:url(https://fonts.gstatic.com/s/voces/v15/-F6_fjJyLyU8d7PGDm_6pClI.woff2) format('woff2');unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}" }, { "url": "https://fonts.googleapis.com/css2?family=Modak", diff --git a/test/integration/image-component/default/pages/blurry-placeholder.js b/test/integration/image-component/default/pages/blurry-placeholder.js index 4ebc925cbe94c..58751c0b9afb3 100644 --- a/test/integration/image-component/default/pages/blurry-placeholder.js +++ b/test/integration/image-component/default/pages/blurry-placeholder.js @@ -15,6 +15,17 @@ export default function Page() { blurDataURL="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400' viewBox='0 0 400 400'%3E%3Cfilter id='blur' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='20' edgeMode='duplicate' /%3E%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='1 1' /%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Cimage filter='url(%23blur)' href='data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMDAwMDAwQEBAQFBQUFBQcHBgYHBwsICQgJCAsRCwwLCwwLEQ8SDw4PEg8bFRMTFRsfGhkaHyYiIiYwLTA+PlT/wAALCAAKAAoBAREA/8QAMwABAQEAAAAAAAAAAAAAAAAAAAcJEAABAwUAAwAAAAAAAAAAAAAFAAYRAQMEEyEVMlH/2gAIAQEAAD8Az1bLPaxhiuk0QdeCOLDtHixN2dmd2bsc5FPX7VTREX//2Q==' x='0' y='0' height='100%25' width='100%25'/%3E%3C/svg%3E" /> + +
{ return (
diff --git a/test/integration/image-component/default/test/index.test.js b/test/integration/image-component/default/test/index.test.js index d2c1b4bc9372b..540383a260608 100644 --- a/test/integration/image-component/default/test/index.test.js +++ b/test/integration/image-component/default/test/index.test.js @@ -669,6 +669,14 @@ describe('Image Component Tests', () => { `background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400' viewBox='0 0 400 400'%3E%3Cfilter id='blur' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='20' edgeMode='duplicate' /%3E%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='1 1' /%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Cimage filter='url(%23blur)' href='data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMDAwMDAwQEBAQFBQUFBQcHBgYHBwsICQgJCAsRCwwLCwwLEQ8SDw4PEg8bFRMTFRsfGhkaHyYiIiYwLTA+PlT/wAALCAAKAAoBAREA/8QAMwABAQEAAAAAAAAAAAAAAAAAAAcJEAABAwUAAwAAAAAAAAAAAAAFAAYRAQMEEyEVMlH/2gAIAQEAAD8Az1bLPaxhiuk0QdeCOLDtHixN2dmd2bsc5FPX7VTREX//2Q==' x='0' y='0' height='100%25' width='100%25'/%3E%3C/svg%3E")` ) + expect($html('#blurry-placeholder')[0].attribs.style).toContain( + `background-position:0% 0%` + ) + + expect( + $html('#blurry-placeholder-tall-centered')[0].attribs.style + ).toContain(`background-position:center`) + expect($html('#blurry-placeholder-with-lazy')[0].attribs.style).toContain( `background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='400' height='400' viewBox='0 0 400 400'%3E%3Cfilter id='blur' filterUnits='userSpaceOnUse' color-interpolation-filters='sRGB'%3E%3CfeGaussianBlur stdDeviation='20' edgeMode='duplicate' /%3E%3CfeComponentTransfer%3E%3CfeFuncA type='discrete' tableValues='1 1' /%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Cimage filter='url(%23blur)' href='data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMDAwMDAwQEBAQFBQUFBQcHBgYHBwsICQgJCAsRCwwLCwwLEQ8SDw4PEg8bFRMTFRsfGhkaHyYiIiYwLTA+PlT/wAALCAAKAAoBAREA/8QAMwABAQEAAAAAAAAAAAAAAAAAAAcJEAABAwUAAwAAAAAAAAAAAAAFAAYRAQMEEyEVMlH/2gAIAQEAAD8Az1bLPaxhiuk0QdeCOLDtHixN2dmd2bsc5FPX7VTREX//2Q==' x='0' y='0' height='100%25' width='100%25'/%3E%3C/svg%3E")` ) diff --git a/test/integration/image-component/default/test/static.test.js b/test/integration/image-component/default/test/static.test.js index 0e431822186a2..666d80d31a703 100644 --- a/test/integration/image-component/default/test/static.test.js +++ b/test/integration/image-component/default/test/static.test.js @@ -38,12 +38,12 @@ const runTests = () => { }) it('Should add a blurry placeholder to statically imported jpg', async () => { expect(html).toContain( - `style="position:absolute;top:0;left:0;bottom:0;right:0;box-sizing:border-box;padding:0;border:none;margin:auto;display:block;width:0;height:0;min-width:100%;max-width:100%;min-height:100%;max-height:100%;filter:blur(20px);background-size:cover;background-image:url("data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAgACAMBIgACEQEDEQH/xAAUAAEAAAAAAAAAAAAAAAAAAAAH/9oACAEBAAAAADX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oACAECEAAAAH//xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDEAAAAH//xAAdEAABAgcAAAAAAAAAAAAAAAATEhUAAwUUIzLS/9oACAEBAAE/AB0ZlUac43GqMYuo/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwB//9k=")"` + `style="position:absolute;top:0;left:0;bottom:0;right:0;box-sizing:border-box;padding:0;border:none;margin:auto;display:block;width:0;height:0;min-width:100%;max-width:100%;min-height:100%;max-height:100%;filter:blur(20px);background-size:cover;background-image:url("data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoKCgoKCgsMDAsPEA4QDxYUExMUFiIYGhgaGCIzICUgICUgMy03LCksNy1RQDg4QFFeT0pPXnFlZXGPiI+7u/sBCgoKCgoKCwwMCw8QDhAPFhQTExQWIhgaGBoYIjMgJSAgJSAzLTcsKSw3LVFAODhAUV5PSk9ecWVlcY+Ij7u7+//CABEIAAgACAMBIgACEQEDEQH/xAAUAAEAAAAAAAAAAAAAAAAAAAAH/9oACAEBAAAAADX/xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oACAECEAAAAH//xAAUAQEAAAAAAAAAAAAAAAAAAAAA/9oACAEDEAAAAH//xAAdEAABAgcAAAAAAAAAAAAAAAATEhUAAwUUIzLS/9oACAEBAAE/AB0ZlUac43GqMYuo/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwB//9k=");background-position:0% 0%"` ) }) it('Should add a blurry placeholder to statically imported png', async () => { expect(html).toContain( - `style="position:absolute;top:0;left:0;bottom:0;right:0;box-sizing:border-box;padding:0;border:none;margin:auto;display:block;width:0;height:0;min-width:100%;max-width:100%;min-height:100%;max-height:100%;filter:blur(20px);background-size:cover;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAQAAABuBnYAAAAATklEQVR42i2I0QmAMBQD869Q9K+IsxU6RkfoiA6T55VXDpJLJC9uUJIzcx+XFd2dXMbx8n+QpoeYDpgY66RaDA83jCUfVpK2pER1dcEUP+KfSBtXK+BpAAAAAElFTkSuQmCC")"` + `style="position:absolute;top:0;left:0;bottom:0;right:0;box-sizing:border-box;padding:0;border:none;margin:auto;display:block;width:0;height:0;min-width:100%;max-width:100%;min-height:100%;max-height:100%;filter:blur(20px);background-size:cover;background-image:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAQAAABuBnYAAAAATklEQVR42i2I0QmAMBQD869Q9K+IsxU6RkfoiA6T55VXDpJLJC9uUJIzcx+XFd2dXMbx8n+QpoeYDpgY66RaDA83jCUfVpK2pER1dcEUP+KfSBtXK+BpAAAAAElFTkSuQmCC");background-position:0% 0%"` ) }) } diff --git a/test/integration/production/test/index.test.js b/test/integration/production/test/index.test.js index 20bd919c4ca0d..591cba62d5b90 100644 --- a/test/integration/production/test/index.test.js +++ b/test/integration/production/test/index.test.js @@ -917,26 +917,30 @@ describe('Production Usage', () => { expect(missing).toBe(false) }) - it('should preserve query when hard navigating from page 404', async () => { - const browser = await webdriver(appPort, '/') - await browser.eval(`(function() { - window.beforeNav = 1 - window.next.router.push({ - pathname: '/non-existent', - query: { hello: 'world' } - }) - })()`) + if (global.browserName !== 'internet explorer') { + it('should preserve query when hard navigating from page 404', async () => { + const browser = await webdriver(appPort, '/') + await browser.eval(`(function() { + window.beforeNav = 1 + window.next.router.push({ + pathname: '/non-existent', + query: { hello: 'world' } + }) + })()`) - await check( - () => browser.eval('document.documentElement.innerHTML'), - /page could not be found/ - ) + await check( + () => browser.eval('document.documentElement.innerHTML'), + /page could not be found/ + ) - expect(await browser.eval('window.beforeNav')).toBe(null) - expect(await browser.eval('window.location.hash')).toBe('') - expect(await browser.eval('window.location.search')).toBe('?hello=world') - expect(await browser.eval('window.location.pathname')).toBe('/non-existent') - }) + expect(await browser.eval('window.beforeNav')).toBe(null) + expect(await browser.eval('window.location.hash')).toBe('') + expect(await browser.eval('window.location.search')).toBe('?hello=world') + expect(await browser.eval('window.location.pathname')).toBe( + '/non-existent' + ) + }) + } if (!process.env.NEXT_PRIVATE_TEST_WEBPACK4_MODE) { it('should remove placeholder for next/image correctly', async () => { diff --git a/test/integration/relay-analytics/test/index.test.js b/test/integration/relay-analytics/test/index.test.js index 38c87d816ea5a..495cdfc974da4 100644 --- a/test/integration/relay-analytics/test/index.test.js +++ b/test/integration/relay-analytics/test/index.test.js @@ -2,7 +2,7 @@ import { join } from 'path' import webdriver from 'next-webdriver' -import { killApp, findPort, nextBuild, nextStart } from 'next-test-utils' +import { killApp, findPort, nextBuild, nextStart, check } from 'next-test-utils' const appDir = join(__dirname, '../') let appPort @@ -59,34 +59,32 @@ describe('Analytics relayer', () => { expect(largestContentfulPaint).not.toBeNaN() expect(largestContentfulPaint).toBeGreaterThan(0) + await check(async () => { + const numBeacons = await browser.eval('window.__BEACONS.length') + return numBeacons === 2 + ? 'success' + : `invalid beacon count: ${numBeacons}` + }, 'success') + const beacons = (await browser.eval('window.__BEACONS')).map(([, value]) => Object.fromEntries(new URLSearchParams(value)) ) - beacons.sort((a, b) => a.event_name.localeCompare(b.event_name)) - expect(beacons.length).toBe(2) - expect(beacons[0]).toMatchObject({ - dsn: 'test', - event_name: 'FCP', - href: expect.stringMatching('http://'), - id: expect.stringContaining('-'), - page: '/', - speed: '4g', - value: expect.stringContaining('.'), - }) - expect(beacons[1]).toMatchObject({ - dsn: 'test', - event_name: 'TTFB', - href: expect.stringMatching('http://'), - id: expect.stringContaining('-'), - page: '/', - speed: '4g', - value: expect.stringContaining('.'), - }) - expect(stdout).toMatch('Next.js Analytics') + for (const beacon of beacons) { + expect(beacon.event_name === 'FCP' || beacon.event_name === 'TTFB').toBe( + true + ) + expect(beacon.dsn).toBe('test') + expect(beacon.href.includes('http://')).toBe(true) + expect(beacon.id.includes('-')).toBe(true) + expect(beacon.page).toBe('/') + expect(beacon.speed).toBe('4g') + expect(isNaN(parseFloat(beacon.value))).toBe(false) + } + expect(stdout).toMatch('Next.js Analytics') await browser.close() }) })