From 2dda7cf2e88481c5287b4f0832488d831f4290b7 Mon Sep 17 00:00:00 2001 From: Keen Yee Liau Date: Tue, 26 Jul 2022 14:17:35 -0700 Subject: [PATCH 1/2] test: cleanup telemetry integration test Instead of constantly parsing the console output, parse the output once, convert the entries in JavaScript objects, then compare them logically rather than textually. --- test/integration/telemetry/test/index.test.js | 246 +++++++++--------- 1 file changed, 121 insertions(+), 125 deletions(-) diff --git a/test/integration/telemetry/test/index.test.js b/test/integration/telemetry/test/index.test.js index d4a810ed7015852..90a886577c57ea6 100644 --- a/test/integration/telemetry/test/index.test.js +++ b/test/integration/telemetry/test/index.test.js @@ -574,11 +574,11 @@ describe('Telemetry CLI', () => { expect(event1).toMatch(`"nextRulesEnabled": {`) expect(event1).toMatch(/"@next\/next\/.+?": "(off|warn|error)"/) - const event2 = /NEXT_BUILD_FEATURE_USAGE[\s\S]+?{([\s\S]+?)}/ - .exec(stderr) - .pop() - expect(event2).toContain(`"featureName": "build-lint"`) - expect(event2).toContain(`"invocationCount": 1`) + const featureUsageEvents = findAllEvents(stderr, 'NEXT_BUILD_FEATURE_USAGE') + expect(featureUsageEvents).toContainEqual({ + featureName: 'build-lint', + invocationCount: 1, + }) }) it(`emits telemetry for lint during build when '--no-lint' is specified`, async () => { @@ -586,13 +586,11 @@ describe('Telemetry CLI', () => { stderr: true, env: { NEXT_TELEMETRY_DEBUG: 1 }, }) - - const event1 = /NEXT_BUILD_FEATURE_USAGE[\s\S]+?{([\s\S]+?)}/ - .exec(stderr) - .pop() - - expect(event1).toContain(`"featureName": "build-lint"`) - expect(event1).toContain(`"invocationCount": 0`) + const events = findAllEvents(stderr, 'NEXT_BUILD_FEATURE_USAGE') + expect(events).toContainEqual({ + featureName: 'build-lint', + invocationCount: 0, + }) }) it(`emits telemetry for lint during build when 'ignoreDuringBuilds' is specified`, async () => { @@ -607,12 +605,11 @@ describe('Telemetry CLI', () => { }) await fs.remove(nextConfig) - const event1 = /NEXT_BUILD_FEATURE_USAGE[\s\S]+?{([\s\S]+?)}/ - .exec(stderr) - .pop() - - expect(event1).toContain(`"featureName": "build-lint"`) - expect(event1).toContain(`"invocationCount": 0`) + const events = findAllEvents(stderr, 'NEXT_BUILD_FEATURE_USAGE') + expect(events).toContainEqual({ + featureName: 'build-lint', + invocationCount: 0, + }) }) it('emits telemetry for `next lint`', async () => { @@ -642,49 +639,32 @@ describe('Telemetry CLI', () => { expect(event1).toMatch(/"@next\/next\/.+?": "(off|warn|error)"/) }) - it('emits telemery for usage of image, script & dynamic', async () => { + it('emits telemery for usage of optimizeFonts, image, script & dynamic', async () => { const { stderr } = await nextBuild(appDir, [], { stderr: true, env: { NEXT_TELEMETRY_DEBUG: 1 }, }) - const regex = /NEXT_BUILD_FEATURE_USAGE[\s\S]+?{([\s\S]+?)}/g - regex.exec(stderr).pop() // optimizeCss - regex.exec(stderr).pop() // nextScriptWorkers - regex.exec(stderr).pop() // build-lint - const optimizeFonts = regex.exec(stderr).pop() - expect(optimizeFonts).toContain(`"featureName": "optimizeFonts"`) - expect(optimizeFonts).toContain(`"invocationCount": 1`) - regex.exec(stderr).pop() // swcLoader - regex.exec(stderr).pop() // swcMinify - regex.exec(stderr).pop() // swcRelay - regex.exec(stderr).pop() // swcStyledComponents - regex.exec(stderr).pop() // swcExperimentalDecorators - regex.exec(stderr).pop() // swcReactRemoveProperties - regex.exec(stderr).pop() // swcRemoveConsole - regex.exec(stderr).pop() // swcImportSource - regex.exec(stderr).pop() // swcEmotion - regex.exec(stderr).pop() // swc/targets/* - regex.exec(stderr).pop() - regex.exec(stderr).pop() - regex.exec(stderr).pop() - regex.exec(stderr).pop() - regex.exec(stderr).pop() - regex.exec(stderr).pop() - regex.exec(stderr).pop() - regex.exec(stderr).pop() - regex.exec(stderr).pop() - regex.exec(stderr).pop() - regex.exec(stderr).pop() - regex.exec(stderr).pop() - const image = regex.exec(stderr).pop() - expect(image).toContain(`"featureName": "next/image"`) - expect(image).toContain(`"invocationCount": 1`) - const script = regex.exec(stderr).pop() - expect(script).toContain(`"featureName": "next/script"`) - expect(script).toContain(`"invocationCount": 1`) - const dynamic = regex.exec(stderr).pop() - expect(dynamic).toContain(`"featureName": "next/dynamic"`) - expect(dynamic).toContain(`"invocationCount": 1`) + const featureUsageEvents = findAllEvents(stderr, 'NEXT_BUILD_FEATURE_USAGE') + expect(featureUsageEvents).toEqual( + expect.arrayContaining([ + { + featureName: 'optimizeFonts', + invocationCount: 1, + }, + { + featureName: 'next/image', + invocationCount: 1, + }, + { + featureName: 'next/script', + invocationCount: 1, + }, + { + featureName: 'next/dynamic', + invocationCount: 1, + }, + ]) + ) }) it('emits telemetry for usage of swc', async () => { @@ -704,42 +684,43 @@ describe('Telemetry CLI', () => { }) await fs.remove(path.join(appDir, 'next.config.js')) await fs.remove(path.join(appDir, 'jsconfig.json')) - - const regex = /NEXT_BUILD_FEATURE_USAGE[\s\S]+?{([\s\S]+?)}/g - regex.exec(stderr).pop() // optimizeCss - regex.exec(stderr).pop() // nextScriptWorkers - regex.exec(stderr).pop() // build-lint - regex.exec(stderr).pop() // optimizeFonts - const swcLoader = regex.exec(stderr).pop() - expect(swcLoader).toContain(`"featureName": "swcLoader"`) - expect(swcLoader).toContain(`"invocationCount": 1`) - const swcMinify = regex.exec(stderr).pop() - expect(swcMinify).toContain(`"featureName": "swcMinify"`) - expect(swcMinify).toContain(`"invocationCount": 1`) - const swcRelay = regex.exec(stderr).pop() - expect(swcRelay).toContain(`"featureName": "swcRelay"`) - expect(swcRelay).toContain(`"invocationCount": 1`) - const swcStyledComponents = regex.exec(stderr).pop() - expect(swcStyledComponents).toContain( - `"featureName": "swcStyledComponents"` - ) - expect(swcStyledComponents).toContain(`"invocationCount": 1`) - const swcReactRemoveProperties = regex.exec(stderr).pop() - expect(swcReactRemoveProperties).toContain( - `"featureName": "swcReactRemoveProperties"` - ) - expect(swcReactRemoveProperties).toContain(`"invocationCount": 1`) - const swcExperimentalDecorators = regex.exec(stderr).pop() - expect(swcExperimentalDecorators).toContain( - `"featureName": "swcExperimentalDecorators"` - ) - expect(swcExperimentalDecorators).toContain(`"invocationCount": 1`) - const swcRemoveConsole = regex.exec(stderr).pop() - expect(swcRemoveConsole).toContain(`"featureName": "swcRemoveConsole"`) - expect(swcRemoveConsole).toContain(`"invocationCount": 1`) - const swcImportSource = regex.exec(stderr).pop() - expect(swcImportSource).toContain(`"featureName": "swcImportSource"`) - expect(swcImportSource).toContain(`"invocationCount": 0`) + const featureUsageEvents = findAllEvents(stderr, 'NEXT_BUILD_FEATURE_USAGE') + expect(featureUsageEvents).toEqual( + expect.arrayContaining([ + { + featureName: 'swcLoader', + invocationCount: 1, + }, + { + featureName: 'swcMinify', + invocationCount: 1, + }, + { + featureName: 'swcRelay', + invocationCount: 1, + }, + { + featureName: 'swcStyledComponents', + invocationCount: 1, + }, + { + featureName: 'swcReactRemoveProperties', + invocationCount: 1, + }, + { + featureName: 'swcExperimentalDecorators', + invocationCount: 1, + }, + { + featureName: 'swcRemoveConsole', + invocationCount: 1, + }, + { + featureName: 'swcImportSource', + invocationCount: 0, + }, + ]) + ) }) it('emits telemetry for usage of `optimizeCss`', async () => { @@ -758,11 +739,11 @@ describe('Telemetry CLI', () => { path.join(appDir, 'next.config.optimize-css') ) - const regex = /NEXT_BUILD_FEATURE_USAGE[\s\S]+?{([\s\S]+?)}/g - regex.exec(stderr).pop() // build-lint - const optimizeCss = regex.exec(stderr).pop() - expect(optimizeCss).toContain(`"featureName": "experimental/optimizeCss"`) - expect(optimizeCss).toContain(`"invocationCount": 1`) + const events = findAllEvents(stderr, 'NEXT_BUILD_FEATURE_USAGE') + expect(events).toContainEqual({ + featureName: 'experimental/optimizeCss', + invocationCount: 1, + }) }) it('emits telemetry for usage of `nextScriptWorkers`', async () => { @@ -781,14 +762,11 @@ describe('Telemetry CLI', () => { path.join(appDir, 'next.config.next-script-workers') ) - const regex = /NEXT_BUILD_FEATURE_USAGE[\s\S]+?{([\s\S]+?)}/g - regex.exec(stderr).pop() // build-lint - regex.exec(stderr).pop() // optimizeCss - const nextScriptWorkers = regex.exec(stderr).pop() - expect(nextScriptWorkers).toContain( - `"featureName": "experimental/nextScriptWorkers"` - ) - expect(nextScriptWorkers).toContain(`"invocationCount": 1`) + const featureUsageEvents = findAllEvents(stderr, 'NEXT_BUILD_FEATURE_USAGE') + expect(featureUsageEvents).toContainEqual({ + featureName: 'experimental/nextScriptWorkers', + invocationCount: 1, + }) }) it('emits telemetry for usage of middleware', async () => { @@ -804,9 +782,12 @@ describe('Telemetry CLI', () => { await fs.remove(path.join(appDir, 'middleware.js')) - const regex = /NEXT_BUILD_OPTIMIZED[\s\S]+?{([\s\S]+?)}/ - const optimizedEvt = regex.exec(stderr).pop() - expect(optimizedEvt).toContain(`"middlewareCount": 1`) + const buildOptimizedEvents = findAllEvents(stderr, 'NEXT_BUILD_OPTIMIZED') + expect(buildOptimizedEvents).toContainEqual( + expect.objectContaining({ + middlewareCount: 1, + }) + ) }) it('emits telemetry for usage of swc plugins', async () => { @@ -828,8 +809,6 @@ describe('Telemetry CLI', () => { env: { NEXT_TELEMETRY_DEBUG: 1 }, }) - console.log(stderr) - await fs.rename( path.join(appDir, 'next.config.js'), path.join(appDir, 'next.config.swc-plugins') @@ -840,20 +819,37 @@ describe('Telemetry CLI', () => { path.join(appDir, 'package.swc-plugins') ) - const regex = /NEXT_SWC_PLUGIN_DETECTED[\s\S]+?{([\s\S]+?)}/g - - const coverage = regex.exec(stderr).pop() - expect(coverage).toContain(`"pluginName": "swc-plugin-coverage-instrument"`) - expect(coverage).toContain(`"pluginVersion": "0.0.6"`) - - const relay = regex.exec(stderr).pop() - expect(relay).toContain(`"pluginName": "@swc/plugin-relay"`) - expect(relay).toContain(`"pluginVersion": "0.2.0"`) - - const absolute = regex.exec(stderr).pop() - expect(absolute).toContain( - `"pluginName": "/test/absolute_path/plugin.wasm"` + const pluginDetectedEvents = findAllEvents( + stderr, + 'NEXT_SWC_PLUGIN_DETECTED' ) - expect(absolute).not.toContain(`pluginVersion`) + expect(pluginDetectedEvents).toEqual([ + { + pluginName: 'swc-plugin-coverage-instrument', + pluginVersion: '0.0.6', + }, + { + pluginName: '@swc/plugin-relay', + pluginVersion: '0.2.0', + }, + { + pluginName: '/test/absolute_path/plugin.wasm', + }, + ]) }) }) + +/** + * Parse the output and return all entries that match the provided `eventName` + * @param {string} output output of the console + * @param {string} eventName + * @returns {Array<{}>} + */ +function findAllEvents(output, eventName) { + const regex = /\[telemetry\] ({.+?^})/gms + // Pop the last element of each entry to retrieve contents of the capturing group + const events = [...output.matchAll(regex)].map((entry) => + JSON.parse(entry.pop()) + ) + return events.filter((e) => e.eventName === eventName).map((e) => e.payload) +} From c492519c16e308be859d0680a7ffd044e2ceed11 Mon Sep 17 00:00:00 2001 From: Keen Yee Liau Date: Tue, 26 Jul 2022 14:23:34 -0700 Subject: [PATCH 2/2] Collect telemetry for next/future/image Track adoption of next/future/image and add relevant test. --- .../next/build/webpack/plugins/telemetry-plugin.ts | 2 ++ packages/next/telemetry/events/build.ts | 1 + test/integration/telemetry/pages/about.js | 5 +++++ test/integration/telemetry/test/index.test.js | 12 ++++++++++++ 4 files changed, 20 insertions(+) diff --git a/packages/next/build/webpack/plugins/telemetry-plugin.ts b/packages/next/build/webpack/plugins/telemetry-plugin.ts index 8bc73c20240e8d3..16526b6f7419229 100644 --- a/packages/next/build/webpack/plugins/telemetry-plugin.ts +++ b/packages/next/build/webpack/plugins/telemetry-plugin.ts @@ -23,6 +23,7 @@ export type SWC_TARGET_TRIPLE = export type Feature = | 'next/image' + | 'next/future/image' | 'next/script' | 'next/dynamic' | 'swcLoader' @@ -59,6 +60,7 @@ interface Connection { // Map of a feature module to the file it belongs in the next package. const FEATURE_MODULE_MAP: ReadonlyMap = new Map([ ['next/image', '/next/image.js'], + ['next/future/image', '/next/future/image.js'], ['next/script', '/next/script.js'], ['next/dynamic', '/next/dynamic.js'], ]) diff --git a/packages/next/telemetry/events/build.ts b/packages/next/telemetry/events/build.ts index e37bea378c7000d..0d4ea009dee5d2c 100644 --- a/packages/next/telemetry/events/build.ts +++ b/packages/next/telemetry/events/build.ts @@ -132,6 +132,7 @@ export type EventBuildFeatureUsage = { // *before* you make changes here. featureName: | 'next/image' + | 'next/future/image' | 'next/script' | 'next/dynamic' | 'experimental/optimizeCss' diff --git a/test/integration/telemetry/pages/about.js b/test/integration/telemetry/pages/about.js index 95d1f9ba9b6ed6f..f35888107b95307 100644 --- a/test/integration/telemetry/pages/about.js +++ b/test/integration/telemetry/pages/about.js @@ -1,4 +1,5 @@ import Image from 'next/image' +import { Image as FutureImage } from 'next/future/image' import profilePic from '../public/small.jpg' function About() { @@ -12,3 +13,7 @@ function About() { } export default About + +export function AboutFutureImage() { + return +} diff --git a/test/integration/telemetry/test/index.test.js b/test/integration/telemetry/test/index.test.js index 90a886577c57ea6..1750ad835551826 100644 --- a/test/integration/telemetry/test/index.test.js +++ b/test/integration/telemetry/test/index.test.js @@ -837,6 +837,18 @@ describe('Telemetry CLI', () => { }, ]) }) + + it('emits telemetry for usage of next/future/image', async () => { + const { stderr } = await nextBuild(appDir, [], { + stderr: true, + env: { NEXT_TELEMETRY_DEBUG: 1 }, + }) + const featureUsageEvents = findAllEvents(stderr, 'NEXT_BUILD_FEATURE_USAGE') + expect(featureUsageEvents).toContainEqual({ + featureName: 'next/future/image', + invocationCount: 1, + }) + }) }) /**