This repository has been archived by the owner on Jul 12, 2019. It is now read-only.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Adds in a proof-of-concept for authoring rules for the Accordion component. Meant primarily for discussion and demonstration.
When setting out to build this, there were a couple of questions I wanted to have answered:
With this PR, I'm hoping to answer the first three questions and will leave some thoughts on the latter, as well.
How does one run the spec on their components?
When using
@carbon/spec
, one will be able to importcreateRunner
or a defaultrunner
to incorporate in their test suite. This runner does not require any testing tools or environment and can run in any DOM-like environment like jsdom. The pattern for using these tools in a test environment would roughly follow:Most likely, this pattern would be superseded by running rules in a component test file. For example,
If you are focusing on a new library and want to incrementally test your implementation, you can use
only
andexclude
to filter the rules that are run. For example:An example of this pattern can be found in
src/rules/__tests__/accordion-test.js
.How does an individual write rules for a component?
Rules are located under
src/rules
. There is an index entrypoint atsrc/rules/index.js
for all rules. Component rules are located undersrc/rules/component-name.js
. Tests for a component are located undersrc/rules/__tests__/component-name-test.js
.In a specific component rule file, there should be an export named
rules
that is of typeArray<Rule>
. EachRule
looks like:The rules are run alongside the runner code shown above. Before each rule, the runner will call
beforeEach
with the given rule context. This hook should return a valid HTMLElement that is rendered in the DOM. OncebeforeEach
is completed,validate
is called for the given rule. Rules can report validation errors by returning a single ValidationError or an array of ValidationErrors.In general, there will be several categories of rules for a component:
Verifying markup
To verify markup, there is a
diff
utility located undersrc/tools/diff.js
. This utility takes in an expected tree and the actual tree and returns back an array of validation errors if there are any mismatches.The expected tree should be built up using either
createExpected
fromsrc/tools/createElement.js
, orspec
fromsrc/tools/html.js
. Both tools are equivalent in their output, howeverspec
allows the creation of a tree using tagged template literals whilecreateExpected
uses function calls. In practice, this looks like:These helpers generate a tree of expected values that we use when compared against an HTMLElement that is rendered in the DOM.
However,
createExpected
andspec
are not tied to having valid DOM values for attributes. Instead, one can pass a custom matcher for a property. This matcher has the signature:For example, we might want to test that the implementation specifies an attribute
aria-controls
that has a value that matches theid
of another node. Using this custom matcher strategy we could verify that the implementation does this as expected. We are currently using this custom matcher strategy inaccordion
to verifyaria-controls
:In the case of
accordion
, we know that the node containingaria-controls
is a sibling to the content panel with theid
. We can then use browser APIs to verify that they match using this custom matcher.Verifying markup changes after interacting with a component
Often times, we will want to write rules that check the rendered HTML after an interaction occurs. For example, with a menu or accordion we would want to verify that after clicking a node that the
aria-expanded
value has been properly updated.Inside of a rule, we can use the default DOM APIs for finding an HTMLElement, calling
.click()
on it, and then making an assertion on ifaria-expanded
. There is an example of this insrc/rules/accordion.js
:Here, we are using
context
to determine how many accordion headers we need to verify. We then will go through each header, click it, and verify the expansion state. If there is a mismatch, we report it usingValidationError
.How does an individual test rules they write for a component?
Surprisingly, this section is very similar to the runner section. We can test our rules by using
createRunner
and filter by a test id using theonly
option. We test our accordion spec inside ofsrc/rules/__tests__/accordion-test.js
using this strategy.Open Questions
createExpected
orspec
? Can it be improved?FAQ
How does error reporting work if there are multiple errors in a rule?
Currently, the strategy is that we will fail fast with our checking. If we encounter an error in a rule, we will exit early so that implementor is not overwhelmed with errors.
Is the format of tests in
src/rules/__tests__/accordion-test.js
what will be followed?What currently exists in the test file is a minimal set of rules to verify the behaviors discussed above. I expect a good number of the checks to be covered by utilities rather than verifying each component spec accurately reports on things like HTML mismatches.