From ca5d0fc1378609bfc3d84c5cc0ffb76aa2e93e0f Mon Sep 17 00:00:00 2001 From: Sean Poulter Date: Wed, 3 Jan 2018 03:59:56 -0500 Subject: [PATCH] Emit stderr message type if identified (#5215) --- packages/jest-editor-support/index.d.ts | 8 ++ packages/jest-editor-support/src/Runner.js | 46 ++++---- .../src/__tests__/runner.test.js | 111 +++++++++++++----- packages/jest-editor-support/src/types.js | 8 ++ 4 files changed, 122 insertions(+), 51 deletions(-) diff --git a/packages/jest-editor-support/index.d.ts b/packages/jest-editor-support/index.d.ts index 6b400f005293..150c175d5f26 100644 --- a/packages/jest-editor-support/index.d.ts +++ b/packages/jest-editor-support/index.d.ts @@ -138,3 +138,11 @@ export interface JestTotalResults { export interface JestTotalResultsMeta { noTestsFound: boolean; } + +export enum MessageTypes { + noTests = 1, + unknown = 0, + watchUsage = 2, +} + +export type MessageType = number; diff --git a/packages/jest-editor-support/src/Runner.js b/packages/jest-editor-support/src/Runner.js index 7c614df56507..eb6bc54efff3 100644 --- a/packages/jest-editor-support/src/Runner.js +++ b/packages/jest-editor-support/src/Runner.js @@ -7,7 +7,8 @@ * @flow */ -import type {Options} from './types'; +import type {Options, MessageType} from './types'; +import {messageTypes} from './types'; import {ChildProcess, spawn} from 'child_process'; import {readFile} from 'fs'; @@ -29,7 +30,7 @@ export default class Runner extends EventEmitter { ) => ChildProcess; watchMode: boolean; options: Options; - prevMessageTypes: number[]; + prevMessageTypes: MessageType[]; constructor(workspace: ProjectWorkspace, options?: Options) { super(); @@ -79,7 +80,6 @@ export default class Runner extends EventEmitter { this.emit('terminalError', message); } else { const noTestsFound = this.doResultsFollowNoTestsFoundMessage(); - this.emit('executableJSON', JSON.parse(data), {noTestsFound}); } }); @@ -90,16 +90,14 @@ export default class Runner extends EventEmitter { }); this.debugprocess.stderr.on('data', (data: Buffer) => { - this.emit('executableStdErr', data); - - const slice = data.toString('utf8', 0, 58); - if ( - slice === 'No tests found related to files changed since last commit.' - ) { - this.prevMessageTypes.push(messageType.noTests); - } else if (/^\s*Watch Usage\b/.test(slice)) { - this.prevMessageTypes.push(messageType.watchUsage); + const type = this.findMessageType(data); + if (type === messageTypes.unknown) { + this.prevMessageTypes.length = 0; + } else { + this.prevMessageTypes.push(type); } + + this.emit('executableStdErr', data, {type}); }); this.debugprocess.on('exit', () => { @@ -142,23 +140,31 @@ export default class Runner extends EventEmitter { delete this.debugprocess; } + findMessageType(buf: Buffer) { + const str = buf.toString('utf8', 0, 58); + if (str === 'No tests found related to files changed since last commit.') { + return messageTypes.noTests; + } + + if (/^\s*Watch Usage\b/.test(str)) { + return messageTypes.watchUsage; + } + + return messageTypes.unknown; + } + doResultsFollowNoTestsFoundMessage() { if (this.prevMessageTypes.length === 1) { - return this.prevMessageTypes[0] === messageType.noTests; + return this.prevMessageTypes[0] === messageTypes.noTests; } if (this.prevMessageTypes.length === 2) { return ( - this.prevMessageTypes[0] === messageType.noTests && - this.prevMessageTypes[1] === messageType.watchUsage + this.prevMessageTypes[0] === messageTypes.noTests && + this.prevMessageTypes[1] === messageTypes.watchUsage ); } return false; } } - -const messageType = { - noTests: 1, - watchUsage: 2, -}; diff --git a/packages/jest-editor-support/src/__tests__/runner.test.js b/packages/jest-editor-support/src/__tests__/runner.test.js index f672407f5f8a..c8b9c0de7908 100644 --- a/packages/jest-editor-support/src/__tests__/runner.test.js +++ b/packages/jest-editor-support/src/__tests__/runner.test.js @@ -14,6 +14,7 @@ const path = require('path'); const {readFileSync} = require('fs'); const fixtures = path.resolve(__dirname, '../../../../fixtures'); import ProjectWorkspace from '../project_workspace'; +import {messageTypes} from '../types'; // Replace `readFile` with `readFileSync` so we don't get multiple threads jest.doMock('fs', () => { @@ -101,28 +102,14 @@ describe('events', () => { expect(mockCreateProcess).toHaveBeenCalledTimes(2); }); - const messageType = { - noTests: 1, - watchUsage: 2, - }; - describe('stdout.on("data")', () => { - it('should indicate when test results follow "No tests found related to files changed since the last commit"', () => { - const listener = jest.fn(); - runner.on('executableJSON', listener); - runner.outputPath = `${fixtures}/failing_jsons/failing_jest_json.json`; - runner.prevMessageTypes.push(messageType.noTests); - fakeProcess.stdout.emit('data', 'Test results written to file'); - - expect(listener.mock.calls[0].length).toBe(2); - expect(listener.mock.calls[0][1]).toEqual({noTestsFound: true}); - }); - - it('should indicate when test results follow "No tests found related to files changed since the last commit" and "Watch Usage"', () => { + it('should emit an "executableJSON" event with the "noTestsFound" meta data property set', () => { const listener = jest.fn(); runner.on('executableJSON', listener); runner.outputPath = `${fixtures}/failing_jsons/failing_jest_json.json`; - runner.prevMessageTypes.push(messageType.noTests, messageType.watchUsage); + (runner: any).doResultsFollowNoTestsFoundMessage = jest + .fn() + .mockReturnValueOnce(true); fakeProcess.stdout.emit('data', 'Test results written to file'); expect(listener.mock.calls[0].length).toBe(2); @@ -131,7 +118,7 @@ describe('events', () => { it('should clear the message type history', () => { runner.outputPath = `${fixtures}/failing_jsons/failing_jest_json.json`; - runner.prevMessageTypes.push(messageType.noTests); + runner.prevMessageTypes.push(messageTypes.noTests); fakeProcess.stdout.emit('data', 'Test results written to file'); expect(runner.prevMessageTypes.length).toBe(0); @@ -139,14 +126,43 @@ describe('events', () => { }); describe('stderr.on("data")', () => { - it('should emit an "executableStdErr" event', () => { + it('should identify the message type', () => { + (runner: any).findMessageType = jest.fn(); + const expected = {}; + fakeProcess.stderr.emit('data', expected); + + expect(runner.findMessageType).toBeCalledWith(expected); + }); + + it('should add the type to the message type history when known', () => { + (runner: any).findMessageType = jest + .fn() + .mockReturnValueOnce(messageTypes.noTests); + fakeProcess.stderr.emit('data', Buffer.from('')); + + expect(runner.prevMessageTypes).toEqual([messageTypes.noTests]); + }); + + it('should clear the message type history when the type is unknown', () => { + (runner: any).findMessageType = jest + .fn() + .mockReturnValueOnce(messageTypes.unknown); + fakeProcess.stderr.emit('data', Buffer.from('')); + + expect(runner.prevMessageTypes).toEqual([]); + }); + + it('should emit an "executableStdErr" event with the type', () => { const listener = jest.fn(); - const expected = Buffer.from(''); + const data = Buffer.from(''); + const type = {}; + const meta = {type}; + (runner: any).findMessageType = jest.fn().mockReturnValueOnce(type); runner.on('executableStdErr', listener); - fakeProcess.stderr.emit('data', expected); + fakeProcess.stderr.emit('data', data, meta); - expect(listener).toBeCalledWith(expected); + expect(listener).toBeCalledWith(data, meta); }); it('should track when "No tests found related to files changed since the last commit" is received', () => { @@ -156,14 +172,7 @@ describe('events', () => { ); fakeProcess.stderr.emit('data', data); - expect(runner.prevMessageTypes).toEqual([messageType.noTests]); - }); - - it('should track when the "Watch Usage" prompt is received', () => { - const data = Buffer.from('\n\nWatch Usage\n...'); - fakeProcess.stderr.emit('data', data); - - expect(runner.prevMessageTypes).toEqual([messageType.watchUsage]); + expect(runner.prevMessageTypes).toEqual([messageTypes.noTests]); }); it('should clear the message type history when any other other data is received', () => { @@ -173,4 +182,44 @@ describe('events', () => { expect(runner.prevMessageTypes).toEqual([]); }); }); + + describe('findMessageType()', () => { + it('should return "unknown" when the message is not matched', () => { + const buf = Buffer.from(''); + expect(runner.findMessageType(buf)).toBe(messageTypes.unknown); + }); + + it('should identify "No tests found related to files changed since last commit."', () => { + const buf = Buffer.from( + 'No tests found related to files changed since last commit.\n' + + 'Press `a` to run all tests, or run Jest with `--watchAll`.', + ); + expect(runner.findMessageType(buf)).toBe(messageTypes.noTests); + }); + + it('should identify the "Watch Usage" prompt', () => { + const buf = Buffer.from('\n\nWatch Usage\n...'); + expect(runner.findMessageType(buf)).toBe(messageTypes.watchUsage); + }); + }); + + describe('doResultsFollowNoTestsFoundMessage()', () => { + it('should return true when the last message on stderr was "No tests found..."', () => { + runner.prevMessageTypes.push(messageTypes.noTests); + expect(runner.doResultsFollowNoTestsFoundMessage()).toBe(true); + }); + + it('should return true when the last two messages on stderr were "No tests found..." and "Watch Usage"', () => { + runner.prevMessageTypes.push( + messageTypes.noTests, + messageTypes.watchUsage, + ); + expect(runner.doResultsFollowNoTestsFoundMessage()).toBe(true); + }); + + it('should return false otherwise', () => { + runner.prevMessageTypes.length = 0; + expect(runner.doResultsFollowNoTestsFoundMessage()).toBe(false); + }); + }); }); diff --git a/packages/jest-editor-support/src/types.js b/packages/jest-editor-support/src/types.js index cedc2628541d..2eabcfa984fd 100644 --- a/packages/jest-editor-support/src/types.js +++ b/packages/jest-editor-support/src/types.js @@ -62,3 +62,11 @@ export type TestAssertionStatus = { export type JestTotalResultsMeta = { noTestsFound: boolean, }; + +export const messageTypes = { + noTests: 1, + unknown: 0, + watchUsage: 2, +}; + +export type MessageType = number;