Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test: Enhance the context with canvas when the test package is used #28368

Merged
merged 25 commits into from
Jul 2, 2024

Conversation

kasperpeulen
Copy link
Contributor

@kasperpeulen kasperpeulen commented Jun 27, 2024

What I did

The CSF pull request is here:
ComponentDriven/csf#99

Following up on #28353
Now the context is always the same reference throughout loaders and the play function, we can enhance the context in the @storybook/test package:

const enhanceContext: LoaderFunction = (context) => {
if (globalThis.HTMLElement && context.canvasElement instanceof globalThis.HTMLElement) {
context.canvas = within(context.canvasElement);
// TODO enable this in a later PR, once we have time to QA this properly
// context.userEvent = userEvent.setup();
}
};

We are adding canvas as just a shortcut for within(canvasElement).

In a later PR we will add userEvent as a shortcut for userEvent.setup() which is a best practice in userEvent, see this issue:
#17988

We use module augmentation to enhance the types when the test package is used:

declare module '@storybook/csf' {
interface Canvas extends Queries {}
interface StoryContext {
// TODO enable this in a later PR, once we have time to QA this properly
// userEvent: ReturnType<typeof userEvent.setup>;
}
}

I needed unbundle the @storybook/csf package to get module augmentation to work.

We won't document this feature until 9.0, but we will use this feature as part of the mount context property. As await mount() will return context.canvas whenever the test package is used. This return type will contain all the queries of testing library.

Testing

The changes in this PR are covered in the following automated tests:

  • stories
  • unit tests
  • integration tests
  • end-to-end tests

Manual testing

This section is mandatory for all contributions. If you believe no manual test is necessary, please state so explicitly. Thanks!

Documentation

  • Add or update documentation reflecting your changes
  • If you are deprecating/removing a feature, make sure to update
    MIGRATION.MD

Checklist for Maintainers

  • When this PR is ready for testing, make sure to add ci:normal, ci:merged or ci:daily GH label to it to run a specific set of sandboxes. The particular set of sandboxes can be found in code/lib/cli/src/sandbox-templates.ts

  • Make sure this PR contains one of the labels below:

    Available labels
    • bug: Internal changes that fixes incorrect behavior.
    • maintenance: User-facing maintenance tasks.
    • dependencies: Upgrading (sometimes downgrading) dependencies.
    • build: Internal-facing build tooling & test updates. Will not show up in release changelog.
    • cleanup: Minor cleanup style change. Will not show up in release changelog.
    • documentation: Documentation only changes. Will not show up in release changelog.
    • feature request: Introducing a new feature.
    • BREAKING CHANGE: Changes that break compatibility in some way with current major version.
    • other: Changes that don't fit in the above categories.

🦋 Canary release

This PR does not have a canary release associated. You can request a canary release of this pull request by mentioning the @storybookjs/core team here.

core team members can create a canary release here or locally with gh workflow run --repo storybookjs/storybook canary-release-pr.yml --field pr=<PR_NUMBER>

Copy link

nx-cloud bot commented Jun 27, 2024

☁️ Nx Cloud Report

CI is running/has finished running commands for commit e9910ce. As they complete they will appear below. Click to see the status, the terminal output, and the build insights.

📂 See all runs for this CI Pipeline Execution


✅ Successfully ran 1 target

Sent with 💌 from NxCloud.

@kasperpeulen kasperpeulen changed the title kasper/canvas Test: Enhance the context with canvas and userEvent when the test package is installed Jun 28, 2024
@kasperpeulen kasperpeulen changed the title Test: Enhance the context with canvas and userEvent when the test package is installed Test: Enhance the context with canvas and userEvent when the test package is used Jun 28, 2024
Comment on lines 22 to 24
interface StoryContext {
userEvent: ReturnType<typeof userEvent.setup>;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want to add canvas here as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Canvas is a special type in CSF, that gets augmented here:

declare module '@storybook/csf' {
interface Canvas extends Queries {}
interface StoryContext {
// TODO enable this in a later PR, once we have time to QA this properly
// userEvent: ReturnType<typeof userEvent.setup>;
}
}

And therefore the StoryContext changes as well:

export interface StoryContext<TRenderer extends Renderer = Renderer, TArgs = Args>
  extends StoryContextForEnhancers<TRenderer, TArgs>,
    Required<StoryContextUpdate<TArgs>> {
  loaded: Record<string, any>;
  abortSignal: AbortSignal;
  canvasElement: TRenderer['canvasElement'];
  hooks: unknown;
  originalStoryFn: StoryFn<TRenderer>;
  viewMode: ViewMode;
  step: StepFunction<TRenderer, TArgs>;
  context: this;
  canvas: Canvas;
}

Copy link
Member

Choose a reason for hiding this comment

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

I don't think canvas should be part of the CSF spec, to be honest. That makes CSF dependent on Testing Library which I don't think is desirable.

Copy link
Contributor Author

@kasperpeulen kasperpeulen Jun 28, 2024

Choose a reason for hiding this comment

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

Right, i agree, but it is not coupled, because canvas is empty interface in CSF, that can be implemented by addons such as @storybook/test.

@kasperpeulen kasperpeulen marked this pull request as ready for review June 28, 2024 12:10
@kasperpeulen kasperpeulen changed the title Test: Enhance the context with canvas and userEvent when the test package is used Test: Enhance the context with canvas when the test package is used Jun 28, 2024
Base automatically changed from kasper/context-prop to next June 28, 2024 13:09
code/addons/links/package.json Outdated Show resolved Hide resolved
@@ -187,8 +187,8 @@ export class StoryRender<TRenderer extends Renderer> implements Render<TRenderer
loaded: {},
step: (label, play) => runStep(label, play, context),
context: null!,
canvas: {},
Copy link
Member

Choose a reason for hiding this comment

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

Doesn't it make more sense to set these to null initially? Or perhaps a getter which throws an error when canvas isn't available because @storybook/test isn't installed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To make module augmentation work, I needed to implement it as an empty interface in CSF. Therefore according to typescript, it can not be null.


export default meta;

export const canvas_is_equal_to_within_canvas_element = {
Copy link
Member

Choose a reason for hiding this comment

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

Why not camelCase?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

because iFindCamelCaseHarderToReadForLongerSentences

// TODO enable this in a later PR, once we have time to QA this properly
// export const context_user_event_is_equal_to_user_event_setup = {
// async play({ userEvent }) {
// await expect(userEvent satisfies typeof globalUserEvent).toEqual(globalUserEvent.setup());
Copy link
Member

Choose a reason for hiding this comment

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

I suspect this won't be equal, as setup() supposedly creates a new instance of UserEvent.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, but toEqual doesn't check for reference. Structurally it was equal.

Co-authored-by: Gert Hengeveld <info@ghengeveld.nl>
@kasperpeulen kasperpeulen merged commit 71d100b into next Jul 2, 2024
52 of 54 checks passed
@kasperpeulen kasperpeulen deleted the kasper/canvas branch July 2, 2024 11:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants