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

Implement visual tests for KButton component #710

Merged

Conversation

KshitijThareja
Copy link
Contributor

Description

This PR aims to introduce concrete visual tests for the KButton component, supplemented by modifications to the existing testing playground file and the renderComponent function to allow for usage of slots in the visual tests.

Issue addressed

#689

Addresses #PR# HERE

Before/after screenshots

Changelog

[#PR no]: PR link

Steps to test

  1. Set the PERCY_TOKEN env variable in your environment.
  2. Run yarn test:visual to execute visual tests
  3. Check Percy dashboard to see visual diffs generated.

At a high level, how did you implement this?

This was accomplished by referring to the Puppeteer documentation for implementing abstract methods for interacting with the components. The changes to the render function and playground file were made in order to render the components if slots were passed from within the tests.

Testing checklist

  • Contributor has fully tested the PR manually
  • If there are any front-end changes, before/after screenshots are included
  • Critical and brittle code paths are covered by unit tests
  • The change is described in the changelog section above

Reviewer guidance

  • Is the code clean and well-commented?
  • Are there tests for this change?
  • Are all UI components LTR and RTL compliant (if applicable)?
  • Add other things to check for here

After review

  • The changelog item has been pasted to the CHANGELOG.md

Comments

Copy link
Contributor

github-actions bot commented Aug 6, 2024

Percy Visual Test Results

Percy Dashboard: View Detailed Report

Environment:

  • Node.js Version: 18.x
  • OS: Ubuntu-latest

Instructions for Reviewers:

  • Click on the Percy Dashboard link to view detailed visual diffs.
  • Review the visual changes highlighted in the report.
  • Approve or request changes based on the visual differences.

<!-- Render default slot if provided -->
<template v-if="slots.default">
<!-- eslint-disable-next-line vue/no-v-html -->
<span v-html="slots.default"></span>
Copy link
Member

Choose a reason for hiding this comment

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

Why should this be in as span? I think a div would be more appropiate in case we need to add more elements inside

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If I take the KButton test with default slot as an example, if you use div, the button will become twice the size, with the upper part showing slot content and the lower part showing the content passed to button itself. That's the reason for using span here.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm I see, but there could be some components where a span could also be a problem. What if we add something like a "wrapperEl" prop that defines the element container, and a "content" that defines the v-html prop? Any other idea is welcome 👐.

Copy link
Member

Choose a reason for hiding this comment

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

I think we can combine both and have something like:

{
  slots: {
    default: {
      element: "div", // or any vue component like KIcon
      elementProps: {
        class: "some-class"
      },
      innerHTML: "<div> Some nested <a>content</a> </div>"
    }
  }
}

</template>
<!-- Render named slots passed to the component -->
<template v-for="(slot, name) in namedSlots" #[name]>
<component :is="slot.content" v-bind="slot.props" :key="name" />
Copy link
Member

@AlexVelezLl AlexVelezLl Aug 14, 2024

Choose a reason for hiding this comment

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

I think it would be better if we follow the same pattern in both default slot and named slot, I would vote to render it inside a v-html. Or in any case, if rendering inside a div gives any error, we can be flexible and provide both options. If slots.name is String, then render it inside a v-html, if not, we can render it as you are doing it here. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Won't the named slots be other components that are being nested in a particular component? In that case they'll always have a name and specific props attached to them just like the parent component, which is being handled correctly in the current scenario.
I thought that default slots are used to handle the case in which slots.name is expected to be a string. I may be wrong though; I am not fully aware of how slots are used throughout KDS.

Copy link
Member

@AlexVelezLl AlexVelezLl Aug 14, 2024

Choose a reason for hiding this comment

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

Not necessarily, we can have html elements that are not vue components as part of named slots (e.g. this #divider in KListWithOverflow, It is a correct Vue syntax, there are actually no differences in the restrictions of what we can do in the default slot vs named slots.

@@ -1,23 +1,24 @@
import percySnapshot from '@percy/puppeteer';

export async function renderComponent(component, props) {
export async function renderComponent(component, props, slots = {}) {
Copy link
Member

Choose a reason for hiding this comment

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

Here we need to add a jsdoc explaining these props, and explaining the structure slots should follow

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, will get it done 👍

@@ -40,8 +41,30 @@ export async function renderComponent(component, props) {
global.expect(isComponentRendered).toBe(true);
}

export async function takeSnapshot(name) {
export async function takeSnapshot(name, options = {}) {
Copy link
Member

Choose a reason for hiding this comment

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

Here would be good also to have some jsdoc, and let users know what options could we use (I think it should be a lot of options that percySnapshot receives, in any case we could link to their documentation).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure 👍

it('renders correctly with default props', async () => {
await renderComponent('KButton', { text: 'Test Button' });
await takeSnapshot('KButton - Default');
it('renders correctly with different appearances', async () => {
Copy link
Member

Choose a reason for hiding this comment

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

IMO each snapshot should be a test case. Unit test cases should be simple and test just one thing. If we want to group multiple test cases like for example with the disable state, we could use a describe block inside de discribe.visual block and group them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, if I am getting it correctly, for the case where we are testing different appearances of KButton for example, we need to write them all in separate it blocks and group those it blocks in one describe block?

Copy link
Member

Choose a reason for hiding this comment

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

Yes, although Im not sure if we should group them as "different appearances", in general all snapshots are different appearances.

Copy link
Member

Choose a reason for hiding this comment

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

Oh different appearances means different values for the appearance prop. I hadnt get it, in that case its up to you if it makes sense to group them, you can group them, if not its fine too.

@KshitijThareja
Copy link
Contributor Author

Hi @AlexVelezLl. I've updated the PR based on the suggestions 👍

Copy link
Member

@AlexVelezLl AlexVelezLl left a comment

Choose a reason for hiding this comment

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

Thanks @KshitijThareja!! Code looks good to me, and snapshots are working as expected! I think we have reach to a very neat and flexible api for slots elements, which is super great. I left just a minor observation, once it is fixed, please feel free to hit that merge button.

We are almost thereee!!! Congratulations 🎉 🎉 🎉 🎉.

primary: true,
appearance: 'raised-button',
});
await takeSnapshot('KButton - Primary Raised Button', { widths: [400], minHeight: 512 });
Copy link
Member

Choose a reason for hiding this comment

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

As we will probably have the same sizes for (almost) all the screenshots of a single component, I think we can make this a constant object defined in the context of the describe.visual block and use the same options object thoughout the tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've made the changes as requested ✅

Copy link
Member

Choose a reason for hiding this comment

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

Thank you!!

@AlexVelezLl AlexVelezLl merged commit 3bddaab into learningequality:gsoc/visual-testing Aug 16, 2024
4 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants