diff --git a/js-api-spec/logger.test.ts b/js-api-spec/logger.test.ts index fe035a91af..1dc3967b23 100644 --- a/js-api-spec/logger.test.ts +++ b/js-api-spec/logger.test.ts @@ -10,215 +10,249 @@ import { Logger, } from 'sass'; -import {skipForImpl, sandbox, captureStdio, captureStdioAsync} from './utils'; +import {sandbox, captureStdio, captureStdioAsync} from './utils'; -skipForImpl('sass-embedded', () => { - it('emits debug to stderr by default', () => { - const stdio = captureStdio(() => { - compileString('@debug heck'); - }); - expect(stdio.out).toBeEmpty(); - expect(stdio.err).not.toBeEmpty(); +it('emits debug to stderr by default', () => { + const stdio = captureStdio(() => { + compileString('@debug heck'); }); + expect(stdio.out).toBeEmpty(); + expect(stdio.err).not.toBeEmpty(); +}); - describe('with @warn', () => { - it('passes the message and stack trace to the logger', done => { - compileString( - `@mixin foo {@warn heck} - @include foo;`, - { - logger: { - warn(message, {deprecation, span, stack}) { - expect(message).toBe('heck'); - expect(span).toBeNull(); - expect(stack).toBeString(); - expect(deprecation).toBeFalse(); - done(); - }, - }, - } - ); - }); - - it('stringifies the argument', done => { - compileString('@warn #abc', { +describe('with @warn', () => { + it('passes the message and stack trace to the logger', done => { + compileString( + ` + @mixin foo {@warn heck} + @include foo; + `, + { logger: { - warn(message) { - expect(message).toBe('#abc'); + warn(message, {deprecation, span, stack}) { + expect(message).toBe('heck'); + expect(span).toBeUndefined(); + expect(stack).toBeString(); + expect(deprecation).toBeFalse(); done(); }, }, - }); + } + ); + }); + + it('stringifies the argument', done => { + compileString('@warn #abc', { + logger: { + warn(message) { + expect(message).toBe('#abc'); + done(); + }, + }, }); + }); - it("doesn't inspect the argument", done => { - compileString('@warn null', { - logger: { - warn(message) { - expect(message).toBe(''); - done(); - }, + it("doesn't inspect the argument", done => { + compileString('@warn null', { + logger: { + warn(message) { + expect(message).toBe(''); + done(); }, - }); + }, }); + }); - it('emits to stderr by default', () => { - const stdio = captureStdio(() => { - compileString('@warn heck'); - }); - expect(stdio.out).toBeEmpty(); - expect(stdio.err).not.toBeEmpty(); + it('emits to stderr by default', () => { + const stdio = captureStdio(() => { + compileString('@warn heck'); }); + expect(stdio.out).toBeEmpty(); + expect(stdio.err).not.toBeEmpty(); + }); - it("doesn't emit warnings with a warn callback", () => { - const stdio = captureStdio(() => { - compileString('@warn heck', { - logger: {warn() {}}, - }); + it("doesn't emit warnings with a warn callback", () => { + const stdio = captureStdio(() => { + compileString('@warn heck', { + logger: {warn() {}}, }); - expect(stdio.out).toBeEmpty(); - expect(stdio.err).toBeEmpty(); }); + expect(stdio.out).toBeEmpty(); + expect(stdio.err).toBeEmpty(); + }); - it('still emits warning with only a debug callback', () => { - const stdio = captureStdio(() => { - compileString('@warn heck', { - logger: {debug() {}}, - }); + it('still emits warning with only a debug callback', () => { + const stdio = captureStdio(() => { + compileString('@warn heck', { + logger: {debug() {}}, }); - expect(stdio.out).toBeEmpty(); - expect(stdio.err).not.toBeEmpty(); }); + expect(stdio.out).toBeEmpty(); + expect(stdio.err).not.toBeEmpty(); + }); - it("doesn't emit warnings with Logger.silent", () => { - const stdio = captureStdio(() => { - compileString('@warn heck', {logger: Logger.silent}); - }); - expect(stdio.out).toBeEmpty(); - expect(stdio.err).toBeEmpty(); + it("doesn't emit warnings with Logger.silent", () => { + const stdio = captureStdio(() => { + compileString('@warn heck', {logger: Logger.silent}); }); + expect(stdio.out).toBeEmpty(); + expect(stdio.err).toBeEmpty(); }); +}); - describe('with @debug', () => { - it('passes the message and span to the logger', done => { - compileString('@debug heck', { - logger: { - debug(message, {span}) { - expect(message).toBe('heck'); - expect(span.start.line).toBe(0); - expect(span.start.column).toBe(0); - expect(span.end.line).toBe(0); - expect(span.end.column).toBe(11); - done(); - }, +describe('with @debug', () => { + it('passes the message and span to the logger', done => { + compileString('@debug heck', { + logger: { + debug(message, {span}) { + expect(message).toBe('heck'); + expect(span.start.line).toBe(0); + expect(span.start.column).toBe(0); + expect(span.end.line).toBe(0); + expect(span.end.column).toBe(11); + done(); }, - }); + }, }); + }); - it('stringifies the argument', done => { - compileString('@debug #abc', { - logger: { - debug(message) { - expect(message).toBe('#abc'); - done(); - }, + it('stringifies the argument', done => { + compileString('@debug #abc', { + logger: { + debug(message) { + expect(message).toBe('#abc'); + done(); }, - }); + }, }); + }); - it('inspects the argument', done => { - compileString('@debug null', { - logger: { - debug(message) { - expect(message).toBe('null'); - done(); - }, + it('inspects the argument', done => { + compileString('@debug null', { + logger: { + debug(message) { + expect(message).toBe('null'); + done(); }, - }); + }, }); + }); - it('emits to stderr by default', () => { - const stdio = captureStdio(() => { - compileString('@debug heck'); + it('emits to stderr by default', () => { + const stdio = captureStdio(() => { + compileString('@debug heck'); + }); + expect(stdio.out).toBeEmpty(); + expect(stdio.err).not.toBeEmpty(); + }); + + it("doesn't emit debugs with a debug callback", () => { + const stdio = captureStdio(() => { + compileString('@debug heck', { + logger: {debug() {}}, }); - expect(stdio.out).toBeEmpty(); - expect(stdio.err).not.toBeEmpty(); }); + expect(stdio.out).toBeEmpty(); + expect(stdio.err).toBeEmpty(); + }); - it("doesn't emit debugs with a debug callback", () => { - const stdio = captureStdio(() => { - compileString('@debug heck', { - logger: {debug() {}}, - }); + it('still emits debugs with only a warn callback', () => { + const stdio = captureStdio(() => { + compileString('@debug heck', { + logger: {warn() {}}, }); - expect(stdio.out).toBeEmpty(); - expect(stdio.err).toBeEmpty(); }); + expect(stdio.out).toBeEmpty(); + expect(stdio.err).not.toBeEmpty(); + }); + + it("doesn't emit debugs with Logger.silent", () => { + const stdio = captureStdio(() => { + compileString('@debug heck', {logger: Logger.silent}); + }); + expect(stdio.out).toBeEmpty(); + expect(stdio.err).toBeEmpty(); + }); +}); + +describe('compile', () => { + it('emits to stderr by default', () => + sandbox(dir => { + dir.write({'style.scss': '@warn heck; @debug heck'}); - it('still emits debugs with only a warn callback', () => { const stdio = captureStdio(() => { - compileString('@debug heck', { - logger: {warn() {}}, - }); + compile(dir('style.scss')); }); expect(stdio.out).toBeEmpty(); expect(stdio.err).not.toBeEmpty(); - }); + })); + + it("doesn't emit to stderr with callbacks", () => + sandbox(dir => { + dir.write({'style.scss': '@warn heck warn; @debug heck debug'}); - it("doesn't emit debugs with Logger.silent", () => { const stdio = captureStdio(() => { - compileString('@debug heck', {logger: Logger.silent}); + compile(dir('style.scss'), { + logger: { + warn(message) { + expect(message).toBe('heck warn'); + }, + debug(message) { + expect(message).toBe('heck debug'); + }, + }, + }); }); expect(stdio.out).toBeEmpty(); expect(stdio.err).toBeEmpty(); + })); +}); + +describe('compileStringAsync', () => { + it('emits to stderr by default', async () => { + const stdio = await captureStdioAsync(async () => { + await compileStringAsync('@warn heck; @debug heck'); }); + expect(stdio.out).toBeEmpty(); + expect(stdio.err).not.toBeEmpty(); }); - describe('compile', () => { - it('emits to stderr by default', () => - sandbox(dir => { - dir.write({'style.scss': '@warn heck; @debug heck'}); - - const stdio = captureStdio(() => { - compile(dir('style.scss')); - }); - expect(stdio.out).toBeEmpty(); - expect(stdio.err).not.toBeEmpty(); - })); - - it("doesn't emit to stderr with callbacks", () => - sandbox(dir => { - dir.write({'style.scss': '@warn heck warn; @debug heck debug'}); - - const stdio = captureStdio(() => { - compile(dir('style.scss'), { - logger: { - warn(message) { - expect(message).toBe('heck warn'); - }, - debug(message) { - expect(message).toBe('heck debug'); - }, - }, - }); - }); - expect(stdio.out).toBeEmpty(); - expect(stdio.err).toBeEmpty(); - })); + it("doesn't emit to stderr with callbacks", async () => { + const stdio = await captureStdioAsync(async () => { + await compileStringAsync('@warn heck warn; @debug heck debug', { + logger: { + warn(message) { + expect(message).toBe('heck warn'); + }, + debug(message) { + expect(message).toBe('heck debug'); + }, + }, + }); + }); + expect(stdio.out).toBeEmpty(); + expect(stdio.err).toBeEmpty(); }); +}); + +describe('compileAsync', () => { + it('emits to stderr by default', () => + sandbox(async dir => { + dir.write({'style.scss': '@warn heck; @debug heck'}); - describe('compileStringAsync', () => { - it('emits to stderr by default', async () => { const stdio = await captureStdioAsync(async () => { - await compileStringAsync('@warn heck; @debug heck'); + await compileAsync(dir('style.scss')); }); expect(stdio.out).toBeEmpty(); expect(stdio.err).not.toBeEmpty(); - }); + })); + + it("doesn't emit to stderr with callbacks", () => + sandbox(async dir => { + dir.write({'style.scss': '@warn heck warn; @debug heck debug'}); - it("doesn't emit to stderr with callbacks", async () => { const stdio = await captureStdioAsync(async () => { - await compileStringAsync('@warn heck warn; @debug heck debug', { + await compileAsync(dir('style.scss'), { logger: { warn(message) { expect(message).toBe('heck warn'); @@ -231,39 +265,5 @@ skipForImpl('sass-embedded', () => { }); expect(stdio.out).toBeEmpty(); expect(stdio.err).toBeEmpty(); - }); - }); - - describe('compileAsync', () => { - it('emits to stderr by default', () => - sandbox(async dir => { - dir.write({'style.scss': '@warn heck; @debug heck'}); - - const stdio = await captureStdioAsync(async () => { - await compileAsync(dir('style.scss')); - }); - expect(stdio.out).toBeEmpty(); - expect(stdio.err).not.toBeEmpty(); - })); - - it("doesn't emit to stderr with callbacks", () => - sandbox(async dir => { - dir.write({'style.scss': '@warn heck warn; @debug heck debug'}); - - const stdio = await captureStdioAsync(async () => { - await compileAsync(dir('style.scss'), { - logger: { - warn(message) { - expect(message).toBe('heck warn'); - }, - debug(message) { - expect(message).toBe('heck debug'); - }, - }, - }); - }); - expect(stdio.out).toBeEmpty(); - expect(stdio.err).toBeEmpty(); - })); - }); + })); }); diff --git a/js-api-spec/utils.ts b/js-api-spec/utils.ts index 5f7522ecb1..f82d5d4909 100644 --- a/js-api-spec/utils.ts +++ b/js-api-spec/utils.ts @@ -2,13 +2,13 @@ // MIT-style license that can be found in the LICENSE file or at // https://opensource.org/licenses/MIT. -import {URL} from 'url'; -import * as util from 'util'; - import 'jest-extended'; -import * as sass from 'sass'; import * as immutable from 'immutable'; +import * as sass from 'sass'; +import * as util from 'util'; import interceptStdout from 'intercept-stdout'; +import {Console} from 'console'; +import {URL} from 'url'; export {sandbox} from './sandbox'; @@ -214,6 +214,16 @@ export function skipForImpl( /** Runs `block` and captures any stdout or stderr it emits. */ export function captureStdio(block: () => void): {out: string; err: string} { + // Jest overrides the console to pipe output to test reporter, so while we + // execute `block` we have to replace Jest's console with a normal one that + // can be intercepted by `interceptStdout`. + const nativeConsole = new Console({ + stdout: process.stdout, + stderr: process.stderr, + }); + const jestConsole = global.console; + global.console = nativeConsole; + let out = ''; let err = ''; const unhook = interceptStdout( @@ -231,6 +241,7 @@ export function captureStdio(block: () => void): {out: string; err: string} { block(); } finally { unhook(); + global.console = jestConsole; } return {out, err}; @@ -240,6 +251,16 @@ export function captureStdio(block: () => void): {out: string; err: string} { export async function captureStdioAsync( block: () => Promise ): Promise<{out: string; err: string}> { + // Jest overrides the console to pipe output to test reporter, so while we + // execute `block` we have to replace Jest's console with a normal one that + // can be intercepted by `interceptStdout`. + const nativeConsole = new Console({ + stdout: process.stdout, + stderr: process.stderr, + }); + const jestConsole = global.console; + global.console = nativeConsole; + let out = ''; let err = ''; const unhook = interceptStdout( @@ -257,6 +278,7 @@ export async function captureStdioAsync( await block(); } finally { unhook(); + global.console = jestConsole; } return {out, err};