Skip to content

Commit

Permalink
test: use cypress component tests (contentful#1196)
Browse files Browse the repository at this point in the history
* test: add Cypress component testing setup

* refactor: turn MultipleReferenceEditor.spec.ts into compontent test

* chore: update eslint plugins

* build: build before running component tests

* chore: path.join webpack filename

* refactor: turn MultipleMediaEditor.spec into compontent test

* refactor: remove obsolete wrapper element

* chore: remove empty function

* refactor: use very basic abstraction for sdk in component tests
  • Loading branch information
anho authored Jul 22, 2022
1 parent db81800 commit 3223ddc
Show file tree
Hide file tree
Showing 23 changed files with 706 additions and 191 deletions.
14 changes: 14 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,19 @@ jobs:
- store_artifacts:
path: cypress/videos

component-tests:
executor: linux-cypress
steps:
- checkout
- yarn_install
- run: yarn build
- run:
name: Run cypress component tests
command: |
yarn cy:run:ct
- store_artifacts:
path: cypress/videos

release:
executor: linux-node
steps:
Expand All @@ -104,6 +117,7 @@ workflows:
jobs:
- lint
- unit-tests
- component-tests
- integration-tests:
name: integration-<< matrix.executor >>-<< matrix.browser >>
matrix:
Expand Down
6 changes: 5 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
module.exports = {
extends: [require.resolve('@contentful/eslint-config-extension')],
extends: [
require.resolve('@contentful/eslint-config-extension'),
require.resolve('@contentful/eslint-config-extension/react.js'),
require.resolve('@contentful/eslint-config-extension/typescript.js'),
],
plugins: ['eslint-plugin-import-helpers'],
rules: {
'import-helpers/order-imports': [
Expand Down
6 changes: 5 additions & 1 deletion cypress.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,9 @@
},
"testFiles": "**/*.spec.*",
"numTestsKeptInMemory": 1,
"ignoreTestFiles": ["**/__snapshots__/*", "**/__image_snapshots__/*"]
"ignoreTestFiles": ["**/__snapshots__/*", "**/__image_snapshots__/*"],
"component": {
"testFiles": "**/*.spec.{js,ts,jsx,tsx}",
"componentFolder": "cypress/component"
}
}
1 change: 1 addition & 0 deletions cypress/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module.exports = {
'mocha/no-mocha-arrows': 'off',
'mocha/no-exclusive-tests': 'error',
'mocha/no-skipped-tests': 'error',
'@typescript-eslint/no-var-requires': 'off',
},
env: {
'cypress/globals': true,
Expand Down
22 changes: 22 additions & 0 deletions cypress/component/mount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';

import { GlobalStyles } from '@contentful/f36-core';
import tokens from '@contentful/f36-tokens';
import { mount as cyMount } from '@cypress/react';
import { MountOptions } from '@cypress/react/dist/mount';
import { Global } from '@emotion/core';

function TestStyles() {
return <Global styles={{ body: { padding: tokens.spacingM } }} />;
}

export function mount(node: React.ReactNode, options?: MountOptions) {
return cyMount(
<>
<GlobalStyles withNormalize={true} />
<TestStyles />
{node}
</>,
options
);
}
133 changes: 133 additions & 0 deletions cypress/component/reference/MultipleEntryReferenceEditor.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
import React from 'react';

import { Card, Heading, Paragraph, Button } from '@contentful/f36-components';
import { Entry } from '@contentful/field-editor-shared';

import { MultipleEntryReferenceEditor } from '../../../packages/reference/src';
import { mount } from '../mount';
import { createReferenceEditorTestSdk } from '../test-sdks';

const commonProps = {
isInitiallyDisabled: false,
parameters: {
instance: {
showCreateEntityAction: true,
showLinkEntityAction: true,
},
},
hasCardEditActions: true,
viewType: 'link',
} as React.ComponentProps<typeof MultipleEntryReferenceEditor>;

describe('Multiple Reference Editor', () => {
const findLinkExistingBtn = () => cy.findByTestId('linkEditor.linkExisting');
const findCustomCards = () => cy.findAllByTestId('custom-card');
const findDefaultCards = () => cy.findAllByTestId('cf-ui-entry-card');

it('is empty by default', () => {
const sdk = createReferenceEditorTestSdk();
mount(<MultipleEntryReferenceEditor {...commonProps} sdk={sdk} />);

findCustomCards().should('not.exist');
findLinkExistingBtn().should('be.enabled');
});

it('renders custom cards', () => {
const sdk = createReferenceEditorTestSdk();
mount(
<MultipleEntryReferenceEditor
{...commonProps}
renderCustomCard={(props) => {
const entity = props.entity as Entry;
const { exField, exDesc } = entity.fields;

if (!exField) {
return false;
}

return (
<Card testId="custom-card">
<Heading>{exField.en}</Heading>
<Paragraph>{exDesc.en}</Paragraph>
<Button onClick={props.onEdit} style={{ marginRight: '10px' }}>
Edit
</Button>
<Button onClick={props.onRemove}>Remove</Button>
</Card>
);
}}
sdk={sdk}
/>
);

findLinkExistingBtn().click();
findCustomCards().should('have.length', 2);
});

it('renders default card instead of custom card', () => {
const sdk = createReferenceEditorTestSdk();
mount(
<MultipleEntryReferenceEditor
{...commonProps}
renderCustomCard={(props) => {
const entity = props.entity as Entry;
const { exField, exDesc } = entity.fields;

if (!exField) {
return false;
}

return (
<Card testId="custom-card">
<Heading>{exField.en}</Heading>
<Paragraph>{exDesc.en}</Paragraph>
<Button onClick={props.onEdit} style={{ marginRight: '10px' }}>
Edit
</Button>
<Button onClick={props.onRemove}>Remove</Button>
</Card>
);
}}
sdk={sdk}
/>
);

findLinkExistingBtn().click();
findLinkExistingBtn().click(); // Inserts another card using standard card renderer.
findDefaultCards().should('have.length', 1);
findCustomCards().should('have.length', 2);
});

it('hides actions when max number of allowed links is reached', () => {
const sdk = createReferenceEditorTestSdk({ validations: [{ size: { max: 3 } }] });
mount(
<MultipleEntryReferenceEditor
{...commonProps}
renderCustomCard={(props) => {
const entity = props.entity as Entry;
const { exField, exDesc } = entity.fields;

if (!exField) {
return false;
}

return (
<Card testId="custom-card">
<Heading>{exField.en}</Heading>
<Paragraph>{exDesc.en}</Paragraph>
<Button onClick={props.onEdit} style={{ marginRight: '10px' }}>
Edit
</Button>
<Button onClick={props.onRemove}>Remove</Button>
</Card>
);
}}
sdk={sdk}
/>
);

findLinkExistingBtn().click(); // inserts 2 cards
findLinkExistingBtn().click(); // inserts 1 card
findLinkExistingBtn().should('not.exist'); // limit reached, button hidden.
});
});
131 changes: 131 additions & 0 deletions cypress/component/reference/MultipleMediaEditor.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import React from 'react';

import { Card, Heading, Button, Asset } from '@contentful/f36-components';

import { MultipleMediaEditor, CombinedLinkActions } from '../../../packages/reference/src';
import { mount } from '../mount';
import { createReferenceEditorTestSdk } from '../test-sdks';

const commonProps = {
isInitiallyDisabled: false,
parameters: {
instance: {
showCreateEntityAction: true,
showLinkEntityAction: true,
},
},
viewType: 'card',
} as React.ComponentProps<typeof MultipleMediaEditor>;

describe('Multiple Media Editor', () => {
const findCreateAndLinkBtn = () => cy.findByTestId('linkEditor.createAndLink');
const findLinkExistingBtn = () => cy.findByTestId('linkEditor.linkExisting');
const findCustomActionsDropdownTrigger = () => cy.findAllByTestId('link-actions-menu-trigger');
const findCards = () => cy.findAllByTestId('cf-ui-asset-card');
const findCustomCards = () => cy.findAllByTestId('custom-card');

describe('default editor', () => {
it('renders default actions', () => {
const sdk = createReferenceEditorTestSdk();
mount(<MultipleMediaEditor {...commonProps} sdk={sdk} />);

findCreateAndLinkBtn().should('exist');
findLinkExistingBtn().should('exist');
});

it('can insert existing links', () => {
const sdk = createReferenceEditorTestSdk();
mount(<MultipleMediaEditor {...commonProps} sdk={sdk} />);

findLinkExistingBtn().click();
findCards().should('have.length', 2);
});

it('can insert new links', () => {
const sdk = createReferenceEditorTestSdk();
mount(<MultipleMediaEditor {...commonProps} sdk={sdk} />);

findCreateAndLinkBtn().click();
findCards().should('have.length', 1);
});
});

describe('custom actions injected actions dropdown', () => {
const validations = [{ size: { max: 2 } }];
const renderCustomActions = (props) => <CombinedLinkActions {...props} />;

it('is rendered', () => {
const sdk = createReferenceEditorTestSdk({ validations });
mount(
<MultipleMediaEditor {...commonProps} sdk={sdk} renderCustomActions={renderCustomActions} />
);

findCustomActionsDropdownTrigger().should('exist');
});

it('is able to interact through props', () => {
const sdk = createReferenceEditorTestSdk({ validations });
mount(
<MultipleMediaEditor {...commonProps} sdk={sdk} renderCustomActions={renderCustomActions} />
);

findCustomActionsDropdownTrigger().click();
findLinkExistingBtn().click();
findCards().should('have.length', 2);
});

it('hides actions when max number of allowed links is reached', () => {
const sdk = createReferenceEditorTestSdk({ validations });
mount(
<MultipleMediaEditor {...commonProps} sdk={sdk} renderCustomActions={renderCustomActions} />
);

findCustomActionsDropdownTrigger().click();
findLinkExistingBtn().click();
findCards().should('have.length', 2);
findCustomActionsDropdownTrigger().should('not.exist');
});
});

describe('custom card', () => {
const renderCustomCard = (props) => {
const title = props.entity.fields.title;
if (!title) {
return false;
}

const file = props.entity.fields.file;
return (
<Card testId="custom-card">
<Heading>{title?.['en-US']}</Heading>
<Asset
src={file?.['en-US'].url}
title={file?.['en-US'].fileName}
style={{ width: 100, height: 100, marginBottom: '10px' }}
/>
<Button onClick={props.onEdit} style={{ marginRight: '10px' }}>
Edit
</Button>
<Button onClick={props.onRemove}>Remove</Button>
</Card>
);
};
it('renders custom cards', () => {
const sdk = createReferenceEditorTestSdk();
mount(<MultipleMediaEditor {...commonProps} sdk={sdk} renderCustomCard={renderCustomCard} />);

findLinkExistingBtn().click();
findCustomCards().should('have.length', 2);
});

it('renders default card instead of custom card', () => {
const sdk = createReferenceEditorTestSdk();
mount(<MultipleMediaEditor {...commonProps} sdk={sdk} renderCustomCard={renderCustomCard} />);

findLinkExistingBtn().click();
findLinkExistingBtn().click(); // Inserts another card using standard card renderer.
findCards().should('have.length', 1);
findCustomCards().should('have.length', 2);
});
});
});
8 changes: 8 additions & 0 deletions cypress/component/test-sdks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { ReferenceEditorSdkProps } from '../../packages/reference/src/__fixtures__/FakeSdk';
import { newReferenceEditorFakeSdk } from '../../packages/reference/src/__fixtures__/FakeSdk';

export function createReferenceEditorTestSdk(props?: ReferenceEditorSdkProps) {
const [sdk] = newReferenceEditorFakeSdk(props);

return sdk;
}
Loading

0 comments on commit 3223ddc

Please sign in to comment.