Automated testing strategies #76
Replies: 5 comments 1 reply
-
I created a test POC branch here: https://github.com/phase2/outline/blob/poc/mdx-stories/src/components/base/outline-alert/test/outline-alert.test.ts Open questions
Loading CSSThe interactive test runner The I got around this with this before(() => {
const defaultCSS = document.createElement('link');
defaultCSS.setAttribute('rel', 'stylesheet');
defaultCSS.setAttribute('src', '/outline.theme.css');
document.head.append(defaultCSS);
}) Loading component JSIf we don't use the I got around this with adding a meaningless test. it('is defined', () => {
const alertElement = document.createElement('outline-alert');
alertElement.setAttribute('statusType', 'error');
// If we don't use OutlineAlert at least once, it's JS will never be loaded and the component won't render. :(
assert.instanceOf(alertElement, OutlineAlert);
document.body.append(alertElement);
const alertElementInDOM = document.querySelectorAll('outline-alert');
expect(alertElementInDOM).to.exist;
}); Waiting for the render of componentsIf we don't wait for components to render, their properties / slots / etc won't be accurately tested. I got around this by waiting a small amount of time. setTimeout(() => {
expect(messageElement.assignedSlot, 'Message is not slotted.').is.not.null;
}, 10); I tried to see if Lit elements emit any events that we could respond to, but I couldn't find anything about that. I tried Could we use CypressJS which automatically retries to avoid these types of brittle tests? Other feedbackI think this test works OK. It doesn't check anything about the implementation so far other than that the slots are slotted. Not sure how to check if text shows up on the screen given that all the text checkers in |
Beta Was this translation helpful? Give feedback.
-
Tried this with CypressJS. Not sure how I feel about it. describe('Outline Alert', () => {
beforeEach(() => {
cy.visit('http://localhost:8000/cypress/fixtures/empty-html');
});
it('Add simple alert', () => {
// This is done inside this wrapper so we are using the document from the application iFrame.
cy.document().then( document => {
const alertElement = document.createElement('outline-alert');
const testMessage = 'Test message';
const messageElement = document.createElement('p');
messageElement.classList.add('message');
messageElement.innerText = testMessage;
alertElement.append(messageElement);
const body = document.querySelector('body') as HTMLBodyElement;
body.append(alertElement);
// If we use a cy.* method, we will let Cypress retry the `expect` a few times.
// This is helpful because we wait for the element to render.
cy.get('.message').should(($messageElement) => {
expect($messageElement.prop('assignedSlot'), 'Message is not slotted.').is.not.null;
});
});
});
}); I saw some examples of trying to load components, but mostly for React, or a complex one for Web Components that I didn't try yet. It's not too easy to interact with the DOM since to keep the benefits of Cypress, you end up using jQuery instead of plain javascript. I do like the test runner in some ways, but in others, it wasn't working according to the documentation. Not sure why, but debugging and pausing was giving me issues. I would probably use our current setup with Web Test Runner and maybe accept the time-out brittleness. :( It's a shame since Cypress has a lot of nice features if we could get it to work like the GUI and snapshots. |
Beta Was this translation helpful? Give feedback.
-
I tried to get the Storyshots addon to work, but ran into a number of issues getting it started.
|
Beta Was this translation helpful? Give feedback.
-
I did more work with Web Test Runner and was able to get import { OutlineModal as OutlineComponent } from '../outline-modal';
import { expect, waitUntil } from '@open-wc/testing';
// We are using these generic names so we can hopefully generalize this process or at least make copy & paste easier.
const componentElementName = 'outline-modal';
describe(componentElementName, () => {
// We don't get base styles automatically. Is there a better way?
const addBaseStyles = () => {
const defaultCSS = document.createElement('link');
defaultCSS.setAttribute('rel', 'stylesheet');
defaultCSS.setAttribute('href', '/outline.theme.css');
document.head.append(defaultCSS);
};
before(() => {
addBaseStyles();
});
it('Make sure to load necessary component JS', () => {
// If we don't use the component at least once, its JS will never be loaded and the component won't render. :(
const componentElement = document.createElement(componentElementName);
expect(componentElement).is.instanceOf(OutlineComponent);
});
it('Slot elements while interacting with the modal.', async () => {
const modalElement = document.createElement(
componentElementName
) as OutlineComponent;
/**
* <button slot="outline-modal--trigger">
* Open small modal.
* </button>
*/
const triggerElement = document.createElement('button');
triggerElement.setAttribute('slot', 'outline-modal--trigger');
triggerElement.innerText = 'Open small modal';
modalElement.append(triggerElement);
/**
* <h2 slot="outline-modal--header">Modal header text</h2>
*/
const headerElement = document.createElement('h2');
headerElement.setAttribute('slot', 'outline-modal--header');
headerElement.innerText = 'Modal header text';
modalElement.append(headerElement);
/**
* <p>Test message</p>
*/
const messageElement = document.createElement('p');
messageElement.innerText = 'Test message';
modalElement.append(messageElement);
document.body.append(modalElement);
await waitUntil(
() => triggerElement.assignedSlot !== null,
'Trigger is slotted'
);
expect(triggerElement.assignedSlot, 'Trigger is slotted.').is.not.null;
expect(headerElement.assignedSlot, 'Header is not yet slotted.').is.null;
expect(messageElement.assignedSlot, 'Message is not yet slotted.').is.null;
modalElement.open();
await waitUntil(() => modalElement.isOpen === true, 'Modal is open');
expect(headerElement.assignedSlot, 'Header is slotted.').is.not.null;
expect(messageElement.assignedSlot, 'Message is slotted.').is.not.null;
modalElement.close();
await waitUntil(() => modalElement.isOpen === false, 'Modal is closed');
expect(headerElement.assignedSlot, 'Header is no longer slotted.').is.null;
expect(messageElement.assignedSlot, 'Message is no longer slotted.').is
.null;
});
}); |
Beta Was this translation helpful? Give feedback.
-
I tried using I keep getting an error which I think is from trying to include a CommonJS module in our TypeScript ESModule world. Tried:
I get errors in the browser like
To get set up I added these to
I also could not get These seem like nice tools if we can get them to work. :( |
Beta Was this translation helpful? Give feedback.
-
We are using Web Test Runner
yarn wtr
to run tests.This uses (among other things)
Discussion topics
What do we want to test?
What limitations have we encountered?
References
Open PR to add testing: #63
Storybook has an overview of how they recommend testing: https://storybook.js.org/docs/web-components/workflows/testing-with-storybook
They break it down into
Beta Was this translation helpful? Give feedback.
All reactions