Skip to content
/ jest Public
forked from jestjs/jest

Commit

Permalink
fix: write snapshots to disk even if FS is mocked
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB committed Dec 24, 2018
1 parent c5fa387 commit 7ff8646
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 26 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
- `[jest-runtime]` Fix mistake as test files when run coverage issue. ([#7506](https://github.com/facebook/jest/pull/7506))
- `[jest-cli]` print info about passWithNoTests flag ([#7309](https://github.com/facebook/jest/pull/7309))
- `[pretty-format]` Omit unnecessary symbol filter for object keys ([#7457](https://github.com/facebook/jest/pull/7457))
- `[jest-snapshot]` Write and read snapshots from disk even if `fs` is mocked ([#7080](https://github.com/facebook/jest/pull/7080))

### Chore & Maintenance

Expand Down
9 changes: 9 additions & 0 deletions e2e/__tests__/__snapshots__/snapshot-mock-fs.test.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`store snapshot even if fs is mocked 1`] = `
"Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 1 written, 1 total
Time: <<REPLACED>>
Ran all test suites."
`;
40 changes: 40 additions & 0 deletions e2e/__tests__/snapshot-mock-fs.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/
'use strict';

const rimraf = require('rimraf');
const path = require('path');
const {extractSummary} = require('../Utils');
const runJest = require('../runJest');

const DIR = path.resolve(__dirname, '../snapshot-mock-fs');
const snapshotDir = path.resolve(DIR, '__tests__/__snapshots__');
const snapshotFile = path.resolve(snapshotDir, 'snapshot.test.js.snap');

beforeEach(() => rimraf.sync(snapshotDir));
afterAll(() => rimraf.sync(snapshotDir));

test('store snapshot even if fs is mocked', () => {
const {json, status, stderr} = runJest.json(DIR, ['--ci=false']);

expect(status).toBe(0);
expect(json.numTotalTests).toBe(1);
expect(json.numPassedTests).toBe(1);

expect(stderr).toMatch('1 snapshot written from 1 test suite.');
expect(extractSummary(stderr).summary).toMatchSnapshot();

// $FlowFixMe dynamic require
const content = require(snapshotFile);
expect(content['snapshot 1']).toBe(`
Object {
"foo": "bar",
}
`);
});
19 changes: 19 additions & 0 deletions e2e/snapshot-mock-fs/__tests__/snapshot.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails oncall+jsinfra
*/
'use strict';

const fs = require('fs');

fs.writeFileSync = jest.fn();

test('snapshot', () => {
const thing = {foo: 'bar'};

expect(thing).toMatchSnapshot();
});
5 changes: 5 additions & 0 deletions e2e/snapshot-mock-fs/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"jest": {
"testEnvironment": "node"
}
}
44 changes: 22 additions & 22 deletions packages/jest-snapshot/src/__tests__/utils.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@
* LICENSE file in the root directory of this source tree.
*/

jest.mock('fs');
jest.mock('../bound_fs', () => ({
boundExistsSync: jest.fn(() => true),
boundReadFile: jest.fn(),
boundWriteFile: jest.fn(),
}));

const fs = require('fs');
const path = require('path');
const chalk = require('chalk');
const boundFs = require('../bound_fs');

const {
getSnapshotData,
Expand All @@ -23,18 +27,8 @@ const {
SNAPSHOT_VERSION_WARNING,
} = require('../utils');

const writeFileSync = fs.writeFileSync;
const readFileSync = fs.readFileSync;
const existsSync = fs.existsSync;
beforeEach(() => {
fs.writeFileSync = jest.fn();
fs.readFileSync = jest.fn();
fs.existsSync = jest.fn(() => true);
});
afterEach(() => {
fs.writeFileSync = writeFileSync;
fs.readFileSync = readFileSync;
fs.existsSync = existsSync;
jest.clearAllMocks();
});

test('keyToTestName()', () => {
Expand All @@ -57,7 +51,7 @@ test('saveSnapshotFile() works with \r\n', () => {
};

saveSnapshotFile(data, filename);
expect(fs.writeFileSync).toBeCalledWith(
expect(boundFs.boundWriteFile).toBeCalledWith(
filename,
`// Jest Snapshot v1, ${SNAPSHOT_GUIDE_LINK}\n\n` +
'exports[`myKey`] = `<div>\n</div>`;\n',
Expand All @@ -71,7 +65,7 @@ test('saveSnapshotFile() works with \r', () => {
};

saveSnapshotFile(data, filename);
expect(fs.writeFileSync).toBeCalledWith(
expect(boundFs.boundWriteFile).toBeCalledWith(
filename,
`// Jest Snapshot v1, ${SNAPSHOT_GUIDE_LINK}\n\n` +
'exports[`myKey`] = `<div>\n</div>`;\n',
Expand All @@ -80,7 +74,9 @@ test('saveSnapshotFile() works with \r', () => {

test('getSnapshotData() throws when no snapshot version', () => {
const filename = path.join(__dirname, 'old-snapshot.snap');
fs.readFileSync = jest.fn(() => 'exports[`myKey`] = `<div>\n</div>`;\n');
boundFs.boundReadFile.mockImplementation(
() => 'exports[`myKey`] = `<div>\n</div>`;\n',
);
const update = 'none';

expect(() => getSnapshotData(filename, update)).toThrowError(
Expand All @@ -95,7 +91,7 @@ test('getSnapshotData() throws when no snapshot version', () => {

test('getSnapshotData() throws for older snapshot version', () => {
const filename = path.join(__dirname, 'old-snapshot.snap');
fs.readFileSync = jest.fn(
boundFs.boundReadFile.mockImplementation(
() =>
`// Jest Snapshot v0.99, ${SNAPSHOT_GUIDE_LINK}\n\n` +
'exports[`myKey`] = `<div>\n</div>`;\n',
Expand All @@ -118,7 +114,7 @@ test('getSnapshotData() throws for older snapshot version', () => {

test('getSnapshotData() throws for newer snapshot version', () => {
const filename = path.join(__dirname, 'old-snapshot.snap');
fs.readFileSync = jest.fn(
boundFs.boundReadFile.mockImplementation(
() =>
`// Jest Snapshot v2, ${SNAPSHOT_GUIDE_LINK}\n\n` +
'exports[`myKey`] = `<div>\n</div>`;\n',
Expand All @@ -141,23 +137,27 @@ test('getSnapshotData() throws for newer snapshot version', () => {

test('getSnapshotData() does not throw for when updating', () => {
const filename = path.join(__dirname, 'old-snapshot.snap');
fs.readFileSync = jest.fn(() => 'exports[`myKey`] = `<div>\n</div>`;\n');
boundFs.boundReadFile.mockImplementation(
() => 'exports[`myKey`] = `<div>\n</div>`;\n',
);
const update = 'all';

expect(() => getSnapshotData(filename, update)).not.toThrow();
});

test('getSnapshotData() marks invalid snapshot dirty when updating', () => {
const filename = path.join(__dirname, 'old-snapshot.snap');
fs.readFileSync = jest.fn(() => 'exports[`myKey`] = `<div>\n</div>`;\n');
boundFs.boundReadFile.mockImplementation(
() => 'exports[`myKey`] = `<div>\n</div>`;\n',
);
const update = 'all';

expect(getSnapshotData(filename, update)).toMatchObject({dirty: true});
});

test('getSnapshotData() marks valid snapshot not dirty when updating', () => {
const filename = path.join(__dirname, 'old-snapshot.snap');
fs.readFileSync = jest.fn(
boundFs.boundReadFile.mockImplementation(
() =>
`// Jest Snapshot v${SNAPSHOT_VERSION}, ${SNAPSHOT_GUIDE_LINK}\n\n` +
'exports[`myKey`] = `<div>\n</div>`;\n',
Expand All @@ -171,7 +171,7 @@ test('escaping', () => {
const filename = path.join(__dirname, 'escaping.snap');
const data = '"\'\\';
saveSnapshotFile({key: data}, filename);
const writtenData = fs.writeFileSync.mock.calls[0][1];
const writtenData = boundFs.boundWriteFile.mock.calls[0][1];
expect(writtenData).toBe(
`// Jest Snapshot v1, ${SNAPSHOT_GUIDE_LINK}\n\n` +
'exports[`key`] = `"\'\\\\`;\n',
Expand Down
16 changes: 16 additions & 0 deletions packages/jest-snapshot/src/bound_fs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

// This file exists so that mocks in userland does not affect snapshots

import fs from 'fs';

export const boundReadFile = fs.readFileSync.bind(fs);
export const boundWriteFile = fs.writeFileSync.bind(fs);
export const boundExistsSync = fs.existsSync.bind(fs);
9 changes: 5 additions & 4 deletions packages/jest-snapshot/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ import type {Path, SnapshotUpdateState} from 'types/Config';

import {getSerializers} from './plugins';
import chalk from 'chalk';
import fs from 'fs';
import mkdirp from 'mkdirp';
import naturalCompare from 'natural-compare';
import path from 'path';
import prettyFormat from 'pretty-format';

import {boundReadFile, boundWriteFile, boundExistsSync} from './bound_fs';

export const SNAPSHOT_VERSION = '1';
const SNAPSHOT_VERSION_REGEXP = /^\/\/ Jest Snapshot v(.+),/;
export const SNAPSHOT_GUIDE_LINK = 'https://goo.gl/fbAQLP';
Expand Down Expand Up @@ -99,9 +100,9 @@ export const getSnapshotData = (
let snapshotContents = '';
let dirty = false;

if (fs.existsSync(snapshotPath)) {
if (boundExistsSync(snapshotPath)) {
try {
snapshotContents = fs.readFileSync(snapshotPath, 'utf8');
snapshotContents = boundReadFile(snapshotPath, 'utf8');
// eslint-disable-next-line no-new-func
const populate = new Function('exports', snapshotContents);
// $FlowFixMe
Expand Down Expand Up @@ -172,7 +173,7 @@ export const saveSnapshotFile = (
);

ensureDirectoryExists(snapshotPath);
fs.writeFileSync(
boundWriteFile(
snapshotPath,
writeSnapshotVersion() + '\n\n' + snapshots.join('\n\n') + '\n',
);
Expand Down

0 comments on commit 7ff8646

Please sign in to comment.