Skip to content
This repository has been archived by the owner on May 3, 2022. It is now read-only.

Commit

Permalink
feat: add build-in Jest for testing (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
suevalov authored Jul 2, 2019
1 parent 7ba72b1 commit 6eb49ce
Show file tree
Hide file tree
Showing 29 changed files with 541 additions and 32 deletions.
3 changes: 2 additions & 1 deletion packages/contentful-extension-scripts/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ const spawn = require('cross-spawn');
const args = process.argv.slice(2);

const scriptIndex = args.findIndex(
x => x === 'build' || x === 'start' || x === 'help'
x => x === 'build' || x === 'start' || x === 'help' || x === 'test'
);
const script = scriptIndex === -1 ? args[0] : args[scriptIndex];
const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : [];

switch (script) {
case 'build':
case 'start':
case 'test':
case 'help': {
const result = spawn.sync(
'node',
Expand Down
6 changes: 6 additions & 0 deletions packages/contentful-extension-scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,20 @@
"url": "https://github.com/contentful/create-contentful-extension/issues"
},
"dependencies": {
"identity-obj-proxy": "3.0.0",
"chalk": "2.4.2",
"cross-spawn": "6.0.5",
"fs-extra": "7.0.1",
"htmlnano": "0.2.3",
"babel-jest": "^24.8.0",
"jest": "^24.8.0",
"jest-watch-typeahead": "^0.3.1",
"ts-jest": "^24.0.2",
"parcel-bundler": "1.12.3",
"parcel-plugin-url-loader": "1.3.1",
"posthtml": "0.11.4",
"posthtml-inline-assets": "3.0.0",
"typescript": "^3.5.2",
"yargs": "13.2.4"
},
"scripts": {
Expand Down
30 changes: 30 additions & 0 deletions packages/contentful-extension-scripts/scripts/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Do this as the first thing so that any code reading it knows the right env.
process.env.BABEL_ENV = 'test';
process.env.NODE_ENV = 'test';
const createJestConfig = require('./utils/createJestConfig');
const paths = require('./utils/paths');
const fs = require('fs');
const jest = require('jest');

const argv = process.argv.slice(2);

let appPackageJson = {};

try {
appPackageJson = fs.readJSONSync(paths.packageJson);
} catch (e) {
// do nothing
}

process.on('unhandledRejection', err => {
throw err;
});

argv.push(
'--config',
JSON.stringify(
Object.assign({}, createJestConfig(), appPackageJson.jest || {})
)
);

jest.run(argv);
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
const paths = require('./paths');

module.exports = function createJestConfig() {
const config = {
verbose: true,
transform: {
'.+\\.(js|jsx)?$': require.resolve('babel-jest'),
'^.+\\.(ts|tsx)?$': require.resolve('ts-jest/dist'),
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': require.resolve(
'./jestFileTransform.js'
),
},
transformIgnorePatterns: [
'[/\\\\]node_modules[/\\\\].+\\.(js|jsx|tsx|ts)$',
],
moduleNameMapper: {
'\\.(css|less|scss)$': require.resolve('identity-obj-proxy'),
},
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}'],
coverageReporters: ['lcov'],
testMatch: ['<rootDir>/**/*.(spec|test).{ts,tsx,js,jsx}'],
testURL: 'http://localhost',
rootDir: paths.root,
coverageDirectory: paths.coverage,
watchPlugins: [
require.resolve('jest-watch-typeahead/filename'),
require.resolve('jest-watch-typeahead/testname'),
],
};

return config;
};
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';

const path = require('path');

// This is a custom Jest transformer turning file imports into filenames.
module.exports = {
process(src, filename) {
const assetFilename = JSON.stringify(path.basename(filename));
return `module.exports = ${assetFilename};`;
},
};
3 changes: 3 additions & 0 deletions packages/contentful-extension-scripts/scripts/utils/paths.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ const appDirectory = fs.realpathSync(process.cwd());
const resolveApp = relativePath => path.resolve(appDirectory, relativePath);

module.exports = {
packageJson: resolveApp('package.json'),
root: resolveApp('.'),
src: resolveApp('src'),
build: resolveApp('build'),
cache: resolveApp('.cache'),
coverage: resolveApp('.coverage'),
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ module.exports = () => {
console.log(chalk.cyan(` npm run build`));
console.log(' Bundles the extension for production.');
console.log();
console.log(chalk.cyan(` npm run test`));
console.log(
' Run jest runner in watch mode. Passes through all flats directly to Jest'
);
console.log();
console.log(chalk.cyan(` npm run configure`));
console.log(
' Asks which space and environment you want to use for development and deployment.'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ module.exports = (pkg, { version, language, type }) => {
pkg.scripts = {
start: 'contentful-extension-scripts start',
build: 'contentful-extension-scripts build',
test: 'contentful-extension-scripts test --env=jsdom --watch',
'test:coverage': 'contentful-extension-scripts test --env=jsdom --coverage',
deploy: 'npm run build && contentful extension update --force',
configure: 'contentful space use && contentful space environment use',
login: 'contentful login',
Expand All @@ -16,12 +18,13 @@ module.exports = (pkg, { version, language, type }) => {
'@babel/preset-env': '7.3.4',
'@babel/preset-react': '7.0.0',
'@contentful/contentful-extension-scripts': version,
'@testing-library/react': '8.0.4',
cssnano: '4.1.10',
'contentful-cli': '0.28.0',
'contentful-cli': '0.33.1',
};
pkg.dependencies = {
'@contentful/forma-36-fcss': '^0.0.20',
'@contentful/forma-36-react-components': '^3.11.2',
'@contentful/forma-36-react-components': '^3.11.3',
'@contentful/forma-36-tokens': '^0.3.0',
'contentful-ui-extensions-sdk': '3.9.0',
'prop-types': '^15.7.2',
Expand All @@ -48,10 +51,11 @@ module.exports = (pkg, { version, language, type }) => {

if (language === 'typescript') {
pkg.devDependencies = Object.assign({}, pkg.devDependencies, {
typescript: '3.5.2',
'@types/jest': '24.0.15',
'@types/react': '^16.8.17',
'@types/react-dom': '^16.8.4',
'@types/webpack-env': '1.13.9',
typescript: '3.5.2',
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
[
"@babel/preset-env",
{
"useBuiltIns": false,
"modules": false
"useBuiltIns": false
}
],
[
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"cmaToken": "",
"managementToken": "",
"activeSpaceId": "",
"activeEnvironmentId": ""
"activeEnvironmentId": "",
"host": "api.contentful.com"
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ yarn-error.log*
# Dependency directories
node_modules/

# Coverage
.coverage

# Build
build/*
!/build/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
} from '@contentful/forma-36-react-components';
import { init, locations } from 'contentful-ui-extensions-sdk';
import '@contentful/forma-36-react-components/dist/styles.css';
import '@contentful/forma-36-fcss';
import '@contentful/forma-36-fcss/dist/styles.css';
import './index.css';

/**
Expand All @@ -25,7 +25,7 @@ import './index.css';
* See https://github.com/contentful/create-contentful-extension/blob/master/docs/examples/entry-editor-content-model.json for details.
*/

class App extends React.Component {
export class App extends React.Component {
constructor(props) {
super(props);

Expand Down Expand Up @@ -71,11 +71,16 @@ class App extends React.Component {
</Paragraph>
<SectionHeading>Title</SectionHeading>
<TextInput
testId="field-title"
onChange={this.onTitleChangeHandler}
value={this.state.title}
/>
<SectionHeading>Body</SectionHeading>
<Textarea onChange={this.onBodyChangeHandler} value={this.state.body} />
<Textarea
testId="field-body"
onChange={this.onBodyChangeHandler}
value={this.state.body}
/>
<SectionHeading>Has abstract?</SectionHeading>
<FieldGroup row={false}>
<RadioButtonField
Expand All @@ -99,6 +104,7 @@ class App extends React.Component {
<React.Fragment>
<SectionHeading>Abstract</SectionHeading>
<Textarea
testId="field-abstract"
onChange={this.onAbstractChangeHandler}
value={this.state.abstract}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';
import { App } from './index';
import { render, cleanup, fireEvent, configure } from '@testing-library/react';

configure({
testIdAttribute: 'data-test-id',
});

function renderComponent(sdk) {
return render(<App sdk={sdk} />);
}

const sdk = {
entry: {
fields: {
title: { getValue: jest.fn(), setValue: jest.fn() },
body: { getValue: jest.fn(), setValue: jest.fn() },
abstract: { getValue: jest.fn(), setValue: jest.fn() },
hasAbstract: { getValue: jest.fn(), setValue: jest.fn() },
},
},
};

describe('App', () => {
beforeEach(() => {
jest.resetAllMocks();
});

afterEach(cleanup);

it('should read a values from entry.fields.*', () => {
sdk.entry.fields.title.getValue.mockReturnValue('title-value');
sdk.entry.fields.body.getValue.mockReturnValue('body-value');
sdk.entry.fields.hasAbstract.getValue.mockReturnValue(true);
sdk.entry.fields.abstract.getValue.mockReturnValue('abstract-value');
const { getByTestId } = renderComponent(sdk);

expect(getByTestId('field-title').value).toEqual('title-value');
expect(getByTestId('field-body').value).toEqual('body-value');
expect(getByTestId('field-abstract').value).toEqual('abstract-value');

fireEvent.change(getByTestId('field-body'), {
target: { value: 'new-body-value' },
});

expect(sdk.entry.fields.body.setValue).toHaveBeenCalledWith(
'new-body-value'
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { init } from 'contentful-ui-extensions-sdk';
import '@contentful/forma-36-react-components/dist/styles.css';
import './index.css';

class App extends React.Component {
export class App extends React.Component {
static propTypes = {
sdk: PropTypes.object.isRequired,
};
Expand All @@ -16,7 +16,7 @@ class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: props.sdk.field.getValue(),
value: props.sdk.field.getValue() || '',
};
}

Expand Down Expand Up @@ -55,6 +55,7 @@ class App extends React.Component {
width="large"
type="text"
id="my-field"
testId="my-field"
value={this.state.value}
onChange={this.onChange}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from 'react';
import { App } from './index';
import { render, fireEvent, cleanup, configure } from '@testing-library/react';

configure({
testIdAttribute: 'data-test-id',
});

function renderComponent(sdk) {
return render(<App sdk={sdk} />);
}

const sdk = {
field: {
getValue: jest.fn(),
onValueChanged: jest.fn(),
setValue: jest.fn(),
removeValue: jest.fn(),
},
window: {
startAutoResizer: jest.fn(),
},
};

describe('App', () => {
beforeEach(() => {
jest.resetAllMocks();
});

afterEach(cleanup);

it('should read a value from field.getValue() and subscribe for external changes', () => {
sdk.field.getValue.mockImplementation(() => 'initial-value');
const { getByTestId } = renderComponent(sdk);

expect(sdk.field.getValue).toHaveBeenCalled();
expect(sdk.field.onValueChanged).toHaveBeenCalled();
expect(getByTestId('my-field').value).toEqual('initial-value');
});

it('should call starstartAutoResizer', () => {
renderComponent(sdk);
expect(sdk.window.startAutoResizer).toHaveBeenCalled();
});

it('should call setValue on every change in input and removeValue when input gets empty', () => {
const { getByTestId } = renderComponent(sdk);

fireEvent.change(getByTestId('my-field'), {
target: { value: 'new-value' },
});

expect(sdk.field.setValue).toHaveBeenCalledWith('new-value');

fireEvent.change(getByTestId('my-field'), {
target: { value: '' },
});

expect(sdk.field.setValue).toHaveBeenCalledTimes(1);
expect(sdk.field.removeValue).toHaveBeenCalledTimes(1);
});
});
Loading

0 comments on commit 6eb49ce

Please sign in to comment.