playwright icon indicating copy to clipboard operation
playwright copied to clipboard

[Feature]: ability to pass components to `HooksConfig`

Open sand4rt opened this issue 1 year ago • 6 comments

🚀 Feature Request

The ability to pass components to HooksConfig, probably by using the resolveImportRef to resolve the hooksConfig;

Example

beforeMount(async ({ app, hooksConfig }) => {
  for (const [name, component] of Object.entries(hooksConfig?.components || {}))
    app.component(name, component);
});
import { test, expect } from '@playwright/experimental-ct-vue';
import DefaultSlot from '@/components/DefaultSlot.vue';
import Button from '@/components/Button.vue';

test('render a component as slot', async ({ mount }) => {
  const component = await mount(DefaultSlot, {
    slots: {
      default: '<Button title="Submit" />',
    },
    hooksConfig: {
      components: { Button }
    }
  });
  await expect(component).toContainText('Submit');
});

Motivation

It simplifies testing slots and other use cases such as providing plugins

sand4rt avatar Apr 21 '24 14:04 sand4rt

Could you help me understand why we need to register component in beforeMount for this use case to work? I was hoping it would work out of the box.

pavelfeldman avatar Apr 22 '24 18:04 pavelfeldman

Vue is unable to locate the <Button /> component when it's passed to the slot as a string.

So this test will fail because there's no reference from the <Button /> string to the actual component:

-- DefaultSlot.vue <-- NOTE: Button component not registered here
<template>
  <main>
    <slot />
  </main>
</template>
test('render a component as slot', async ({ mount }) => {
  const component = await mount(DefaultSlot, {
    slots: {
      default: '<Button title="Submit" />', <-- NOTE: There is no reference
    },
  });
  await expect(component).toContainText('Submit');
})

Vue test utils follows a similar approach: https://test-utils.vuejs.org/api/#global-components. Their mount.global is kind of similar to Playwright's mount.hooksConfig: https://test-utils.vuejs.org/api/#global

sand4rt avatar Apr 22 '24 18:04 sand4rt

Ah, I missed the string quotes around <Button>, it makes sense to me now. In terms of the proposed shape of the API, I see hooksConfig as a user object w/o schema. In this case it seems like Vue would benefit from framework-specific components property that would point to the component registry. Does it make sense?

pavelfeldman avatar Apr 22 '24 21:04 pavelfeldman

I see hooksConfig as a user object w/o schema

I actually use hooksConfig specifically for registering global things like mixins, components, plugins and directives. It's been a while, but hooksConfig was introduced two years ago for this reason right?: https://github.com/microsoft/playwright/issues/14416

In this case it seems like Vue would benefit from framework-specific components property that would point to the component registry. Does it make sense?

It is a common scenario, but i don't think it needs a specific API as it saves just a few lines of code. This would also be the first API that's different from the rest and i kind of like the flexibility of the hooksConfig.

It's a trade-off between a generic hooksConfig API or framework-specific APIs IMO. I recently sent something about this on Discord. With the Angular adapter we are also confronted with the question: 'domain specific or generic'

sand4rt avatar Apr 23 '24 16:04 sand4rt

Another option is to put the boilerplate code in playwright-create:

// /playwright/index.ts
beforeMount(async ({ app, hooksConfig }) => {
  for (const [name, component] of Object.entries(hooksConfig?.components || {}))
    app.component(name, component);
});

sand4rt avatar Apr 23 '24 17:04 sand4rt

Ah, so you want the user to control both sides of the story, so hooksConfig's schema is left to the user to define? That's fine with me.

pavelfeldman avatar Apr 23 '24 19:04 pavelfeldman