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(common): add prop theme utility for E2E #9027

Merged
merged 11 commits into from
Apr 8, 2024

Conversation

alisonailea
Copy link
Contributor

Related Issue: #7180

Summary

Our chromium tests do a good job of ensuring the component token updates to styles do not break the existing UI but custom style application of tokens should be considered a data change, not a style change and therefor should be tested with Unit tests. These E2E tests are faster, less resource heavy, and less prone to human error.

However, E2E token tests are harder to set up. To facilitate ease-of-use for other developers this PR introduces a new commonTest utility called themed. This utility expects a calcite-component followed by an object of component token names as keys and each value being an object with a selector, shadowSelector, targetProp, and optional state. The state key will accept a string or an object for more selector specificity which is required when testing a "press"/"active" state.

@alisonailea alisonailea requested a review from a team as a code owner April 1, 2024 19:25
@alisonailea alisonailea changed the title tests: add theming common test utils ci: add common utils for E2E component theme tests Apr 1, 2024
@alisonailea alisonailea changed the title ci: add common utils for E2E component theme tests test: add common utils for E2E component theme tests Apr 1, 2024
@alisonailea alisonailea changed the title test: add common utils for E2E component theme tests test(common): add prop theme utility for E2E Apr 1, 2024
Copy link
Member

@jcfranco jcfranco left a comment

Choose a reason for hiding this comment

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

Looking good! 😎

* Helper to test custom theming of a component's associated tokens.
*
* @example
* describe("default", () => {
Copy link
Member

Choose a reason for hiding this comment

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

Is the default name intentional? All other utils use a name closer to the test subject.

{
selector: string;
shadowSelector?: string;
targetProp: string;
Copy link
Member

Choose a reason for hiding this comment

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

Can you type this as keyof CSSStyleDeclaration?

): void {
it("is theme-able", async () => {
const page = await simplePageSetup(componentTagOrHTML);
const expectChecklist: Record<
Copy link
Member

Choose a reason for hiding this comment

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

Can we add explicit types for these objects?

}
>,
): void {
it("is theme-able", async () => {
Copy link
Member

Choose a reason for hiding this comment

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

Should this be themeable?

targetProps[token] = [targetProp, themedTokenValue, state];
}

// let all page.find calls resolve
Copy link
Member

Choose a reason for hiding this comment

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

This isn't quite accurate, find does not rely on waitForChanges.

await page.mouse.down();
}
if (stateName === "focus") {
await page.mouse.up();
Copy link
Member

Choose a reason for hiding this comment

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

Should this also call mouse.down or does this depend on press being specified before? If it's the latter, it might be prone to being missed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks for the catch

for (const [targetProp, themedTokenValue, state] of props) {
if (state) {
if (typeof state === "string") {
await target[state]();
Copy link
Member

Choose a reason for hiding this comment

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

Can we use a type to hint at which E2EElement methods are used here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

refactored this so it's simplified and more clear

*
* @param page - the e2e page
* @param target - the element to get the computed style from
* @param selector - the selector of the target element
Copy link
Member

Choose a reason for hiding this comment

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

selector can be removed as there is no matching prop in the function signature.

export async function assertThemedProps(
page: E2EPage,
target: E2EElement,
props: [string, string, string | Record<string, { attribute: string; value: string }> | undefined][],
Copy link
Member

Choose a reason for hiding this comment

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

Is the type accurate? I'm not sure I spot where the 3rd item (Record<string, { attribute: string; value: string }> is provided in themed. The doc mentions a second value, but there isn't a mention of the third one.

On a related note, can we use an object vs a tuple? It's not clear from the type itself what the tuple contains. Alternatively, props could be renamed to make this clearer. It's slightly misleading as it has both the prop and value.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah this was silly. Refactored to be much more simple and clear.

for (let i = 0; i < shadowSelectors.length; i++) {
const s = shadowSelectors[i];

if (i === 0) {
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I follow this. Could you shed some light on how this works?

Also, has custom deep piercing been working smoothly? I recall this wasn't working before due to a Stencil bug that was recently fixed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This uses the Stencil bug work around that you posted. It does expect a certain pattern to the shadowSelector but it does work.

[shadowSelector] [wrapper-class] [sub-element] >>> [wrapper-class] [sub-element] >>> [shadowSelector]

Copy link
Member

@jcfranco jcfranco left a comment

Choose a reason for hiding this comment

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

Approving to get component E2E testing moving along. Any updates to the helper shouldn't impact the rest (famous last words 🌻💀🌻).

for (const token in tokens) {
let selectors = tokens[token];

if (!isArray(selectors)) {
Copy link
Member

Choose a reason for hiding this comment

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

Can you use Array.isArray instead of adding a custom function for this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no. The custom type guard is required. See microsoft/TypeScript#19892

Copy link
Member

Choose a reason for hiding this comment

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

Are you seeing incorrect types or a particular TS error? The issue you referenced is related to ReadOnlyArray type, which we are not using.

Types seem fine in both code paths.

Screenshot 2024-04-05 at 11 05 56 AM

Screenshot 2024-04-05 at 11 11 24 AM

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That doesn't work for me. It says it doesn't know if it's a string or an array.

* @param options.state - the state to apply to the target element
* @param options.expectedValue - the expected value of the targetProp
*/
export async function assertThemedProps(page: E2EPage, options: TestTarget): Promise<void> {
Copy link
Member

Choose a reason for hiding this comment

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

Does this need to be exported?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

why wouldn't we export it? It might be useful in other contexts and costs us nothing to export.

Copy link
Member

Choose a reason for hiding this comment

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

It's not used at the moment and bypasses the current way we're recommending component tokens to be tested via themed.

* @param token - the token as a CSS variable
* @returns string - the new value for the token
*/
export function assignTestTokenThemeValues(token: string): string {
Copy link
Member

Choose a reason for hiding this comment

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

Can we move this into commonTests? #9056 should help split up helpers by test topic.

@alisonailea alisonailea merged commit dff8460 into epic/7180-component-tokens Apr 8, 2024
4 checks passed
@alisonailea alisonailea deleted the astump/7180-add-e2e-tests branch April 8, 2024 17:52
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