Skip to content

Commit

Permalink
Setup tests
Browse files Browse the repository at this point in the history
  • Loading branch information
hansottowirtz committed Dec 30, 2022
1 parent 26a55c6 commit 6421bc2
Show file tree
Hide file tree
Showing 6 changed files with 2,133 additions and 228 deletions.
18 changes: 18 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: Storybook Tests
on: deployment_status
jobs:
test:
timeout-minutes: 60
runs-on: ubuntu-latest
if: github.event.deployment_status.state == 'success'
steps:
- uses: actions/checkout@v3
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: "18.x"
- run: yarn
- run: yarn build
- run: yarn test
env:
TARGET_URL: "${{ github.event.deployment_status.target_url }}"
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
"build": "tsc && tsup src/index.ts",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build",
"test": "npm run test-storybook",
"test-storybook": "test-storybook",
"prepublishOnly": "npm run build"
},
"files": [
Expand All @@ -30,9 +32,11 @@
"@storybook/addon-essentials": "^7.0.0-alpha.57",
"@storybook/addon-interactions": "^7.0.0-alpha.57",
"@storybook/addon-links": "^7.0.0-alpha.57",
"@storybook/jest": "^0.0.11-next.0",
"@storybook/react": "^7.0.0-alpha.57",
"@storybook/react-vite": "^7.0.0-alpha.57",
"@storybook/testing-library": "^0.0.13",
"@storybook/test-runner": "^0.10.0-next.4",
"@storybook/testing-library": "^0.0.14-next.1",
"@types/react": "^18.0.26",
"@types/react-dom": "^18.0.9",
"@typescript-eslint/eslint-plugin": "^5.45.0",
Expand Down
72 changes: 62 additions & 10 deletions src/stories/Button.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,71 @@
import { action } from "@storybook/addon-actions";
import type { Meta } from "@storybook/react";
import { within, fireEvent } from "@storybook/testing-library";
import { expect } from "@storybook/jest";
import Button from "../components/Button";
import { ContextualStoryDecorator } from "./StoryWrapper";
import { createSpyableActions } from "./common";

const { actions, actionsMockReset } = createSpyableActions();

export default {
title: "Contextual API/Button",
component: Button,
decorators: [
ContextualStoryDecorator({
onDone: action("done"),
onAbort: action("abort"),
onError: action("error"),
onPending: action("pending"),
}),
],
decorators: [ContextualStoryDecorator(actions)],
} as Meta;

export const Primary = { args: { children: "Click me", persistDone: false } };
export const Primary: Meta = {
args: { children: "Click me", persistDone: false },
};

export const InteractionTestNotPersisted: Meta = {
args: { children: "Click me", persistDone: false },
play: async ({ canvasElement }) => {
await Promise.resolve();

actionsMockReset();

const canvas = within(canvasElement);
const button = canvas.getByText("Click me", { selector: "button" });
fireEvent.click(button);
const text = canvas.getByTestId("root-state");
expect(text.innerText).toBe("pending");
await Promise.resolve();
// it is resolved in 1 microtask
expect(text.innerText).toBe("done");
const refreshButton = canvas.getByTestId("new-root-done-tracker");
fireEvent.click(refreshButton);
await Promise.resolve();
expect(text.innerText).toBe("pending");
fireEvent.click(button);
expect(text.innerText).toBe("pending");
await Promise.resolve();
expect(text.innerText).toBe("done");
},
};

export const InteractionTestPersisted: Meta = {
args: { children: "Click me", persistDone: true },
play: async ({ canvasElement }) => {
await Promise.resolve();

actionsMockReset();

const canvas = within(canvasElement);
const text = await canvas.findByTestId("root-state");
const refreshButton = await canvas.findByTestId("new-root-done-tracker");

const button = canvas.getByText("Click me", { selector: "button" });
fireEvent.click(button);
expect(text.innerText).toBe("pending");
await Promise.resolve();
// it is resolved in 1 microtask
expect(text.innerText).toBe("done");
expect(actions.onDone).toBeCalledTimes(1);
actions.onDone.mockReset();
fireEvent.click(refreshButton);
await Promise.resolve();
expect(text.innerText).toBe("done");
expect(actions.onPending).not.toBeCalled();
expect(actions.onDone).toBeCalledTimes(1);
},
};
7 changes: 5 additions & 2 deletions src/stories/StoryWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,12 @@ export default function StoryWrapper(props: StoryWrapperProps) {
}}
>
<span style={{ marginRight: 4, fontSize: 14 }}>
Root state: {status}
Root state: <span data-testid="root-state">{status}</span>
</span>
<button onClick={() => forceRefreshRef.current?.()}>
<button
onClick={() => forceRefreshRef.current?.()}
data-testid="new-root-done-tracker"
>
🔄 New done tracker
</button>
</div>
Expand Down
29 changes: 29 additions & 0 deletions src/stories/common.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { action } from "@storybook/addon-actions";
import { jest } from "@storybook/jest";

export const actions = {
onDone: action("done"),
onAbort: action("abort"),
onError: action("error"),
onPending: action("pending"),
};

export function createSpyableActions() {
const spyableActions: Record<
keyof typeof actions,
jest.Mock<void, []>
> = Object.fromEntries(
Object.entries(actions).map(([k, v]) => {
const fn = jest.fn(v);
// set function name for storybook interaction view
Object.defineProperty(fn, "name", { value: k });
return [k as any, fn] as const;
})
);

const actionsMockReset = () => {
Object.values(spyableActions).forEach((action) => action.mockReset());
};

return { actions: spyableActions, actionsMockReset };
}
Loading

1 comment on commit 6421bc2

@vercel
Copy link

@vercel vercel bot commented on 6421bc2 Dec 30, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.