-
Notifications
You must be signed in to change notification settings - Fork 843
refactor: make element spec processing more cosistent #4093
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
Conversation
|
|
||
| var iframe = document.createElement('iframe'); | ||
| iframe.src = '/test/mock/frames/context.html'; | ||
| iframe.src = '/test/mock/frames/noHtml-config.html'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The original test was configureing in the top frame but not the child frame. This isn't supported and doesn't provide the protection noHtml is intended to (the thing that the test after this one is verifying). However, previously, the original test worked despite testing an unsupported configuration because there was an extra round trip through DqElement in the top frame involved that no longer exists.
I think it's preferable to break the old pattern and demand that configure must be called consistently in each frame, so I changed the test rather than forcing a top-frame-only noHtml configure to suppress html properties from child frame results, but if that feels too concerning for backcompat reasons you could address it by adding an audit.noHtml test into the process-aggregate.js code that's filling in html from node.source.
|
Should the default serializer take into account options like options.selectors? I wonder if this could have bad performance implications. Otherwise I like that we can add custom element data to results with this. One problem I see is that custom properties wouldn't be copied over in the reporter processAggregate, so if we use this we would need to implement a custom reporter (probably having to re-implement everything in processAggregate). |
811d4aa to
9c48f1c
Compare
| assert.isNull(passes[0].nodes[0].any[0].relatedNodes[0].html); | ||
| assert.isNull(violations[0].nodes[0].any[0].relatedNodes[0].html); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prior to this, node.html would be null, but relatedNode.html was "Undefined". That was a bug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably need to add the setRunOptions to run virtual rule as well.
lib/core/utils/node-serializer.js
Outdated
| serializer = newSerializer; | ||
| } | ||
|
|
||
| Object.assign(nodeSerializer, { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding functions onto another function hides those functions from the user. They won't get code completion nor typescript support for this. Is there a reason you didn't want to have nodeSerializer be an object with a setSerializer function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why wouldn't you be able to type / get intellisense on this? I think this would do it:
type NodeToSpec = <T extends SerialDqElement = SerialDqElement>
(node: VirtualNode | Element) => T;
type MergeSpecs = <T extends SerialDqElement = SerialDqElement>
(parentSpec: SerialDqElement, childSpec: SerialDqElement) => T;
interface Utils {
nodeSerializer: {
({ isSpec, mergeSpecs }: { isSpec?: NodeToSpec, mergeSpecs?: MergeSpecs }): void,
isSpec: NodeToSpec,
mergeSpecs: NodeToSpec
}But sure, this could also just be an object with a "set" function if you want. I like this approach better personally, but it's not a big deal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed, I'm replacing this with a .update() method.
…re into dbjorge/node-serializer
This isn't useful, as the only time DqElement will be part of runVirtualRule is if that vNode includes an actualNode. But then if you do that, this throws because const fixture = document.querySelector('#fixture')
fixture.innerHTML = '<i role="invalid-role"></i>'
const vNode = axe.setup(fixture.firstElementChild);
const result = axe.runVirtualRule('aria-roles', vNode);
// TypeError: Cannot read properties of undefined (reading 'role="invalid-role"')If there's ever a use case for it, solving for it belongs in a different PR. |
cebf827 to
d1b07e1
Compare
|
@WilcoFiers I just have the two most recent unresolved comments remaining (it's technically my PR so I can't "request changes") |
e546e14 to
c59ad63
Compare
Co-authored-by: Steven Lambert <[email protected]>
|
LGTM |
| * suitable for JSON.stringify to consume. Output must include all properties | ||
| * that DqElement.toJSON() would have. Will always be invoked from the | ||
| * input element's original page context. | ||
| * @property {Function} mergeSpecs (Optional) Merges two specs (produced by toSpec) which |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this also include a note about needing to pass through all properties that DqElement.mergeSpec would pass through (ancestry, selector, etc.) just like the note for toSpec?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that's crucial, but if you have a suggestion I'll put it in 😁
Co-authored-by: Steven Lambert <[email protected]>
This API was added in pr dequelabs#4093, but TS definitions were never added.
This API was added in pr dequelabs#4093, but TS definitions were never added.
This API was added in pr #4093, but TS definitions were never added. For simplicity I'm using SerialDqElement in the API. We could introduce a generic for the custom serialized type (T extends SerialDqElement), but it's hard to consistently use it everywhere (AxeReporter, NodeSerializer.dqElmToSpec). I also fixed DqElement.mergeSpecs which is needed to implement a custom node serializer: it exists on the constructor and not on the individual instances
## [4.11.0](v4.10.3...v4.11.0) (2025-10-07) ### Features - add RGAA tags to rules ([#4862](#4862)) ([53a925a](53a925a)) - **aria-prohibited-attr:** add support for fallback roles ([#4325](#4325)) ([62a19a9](62a19a9)) - **axe.d.ts:** add nodeSerializer typings ([#4551](#4551)) ([a2f3a48](a2f3a48)), closes [#4093](#4093) - **DqElement:** deprecate fromFrame function ([#4881](#4881)) ([374c376](374c376)), closes [#4093](#4093) - **DqElement:** Truncate large `html` strings when the element has a large outerHTML string ([#4796](#4796)) ([404a4fb](404a4fb)), closes [#4544](#4544) - **get-xpath:** return proper relative selector for id ([#4846](#4846)) ([1035f9e](1035f9e)), closes [#4845](#4845) - **i18n:** Add Portugal Portuguese translation ([#4725](#4725)) ([5b6a65a](5b6a65a)) - incomplete with node on which an error occurred ([#4863](#4863)) ([32ed8da](32ed8da)) - **locale:** Added ru locale ([#4565](#4565)) ([067b01d](067b01d)) - **tap:** some best practice rules map to RGAA ([#4895](#4895)) ([bc33f4c](bc33f4c)) - **td-headers-attr:** report headers attribute referencing other <td> elements as unsupported ([#4589](#4589)) ([ec7c6c8](ec7c6c8)), closes [#3987](#3987) ### Bug Fixes - **aria-allowed-role:** add form to allowed roles of form element ([#4588](#4588)) ([8aa47ac](8aa47ac)), closes [/github.com/dequelabs/axe-core/blob/develop/lib/standards/html-elms.js#L264](https://github.com/dequelabs//github.com/dequelabs/axe-core/blob/develop/lib/standards/html-elms.js/issues/L264) - **aria-allowed-role:** Add math to allowed roles for img element ([#4658](#4658)) ([95b6c18](95b6c18)), closes [#4657](#4657) - **autocomplete-valid :** Ignore readonly autocomplete field ([#4721](#4721)) ([491f4ec](491f4ec)), closes [#4708](#4708) - **autocomplete-valid:** treat values "xon" and "xoff" as non-WCAG-violations ([#4878](#4878)) ([52bc611](52bc611)), closes [#4877](#4877) - **axe.d.ts:** add typings for preload options object ([#4543](#4543)) ([cfd2974](cfd2974)) - **button-name,input-button-name,input-img-alt:** allow label to give accessible name ([#4607](#4607)) ([a9710d7](a9710d7)), closes [#4472](#4472) [#3696](#3696) [#3696](#3696) - **captions:** fix grammar in captions check incomplete message ([#4661](#4661)) ([11de515](11de515)) - **color-contrast:** do not run on elements with font-size: 0 ([#4822](#4822)) ([d77c885](d77c885)), closes [#4820](#4820) - consistently parse tabindex, following HTML 5 spec ([#4637](#4637)) ([645a850](645a850)), closes [#4632](#4632) - **core:** measure perf for async checks ([#4609](#4609)) ([7e9bacf](7e9bacf)) - fix grammar when using "alternative text" in a sentence ([#4811](#4811)) ([237a586](237a586)), closes [#4394](#4394) - **get-ancestry:** add nth-child selector for multiple siblings of shadow root ([#4606](#4606)) ([1cdd6c3](1cdd6c3)), closes [#4563](#4563) - **get-ancestry:** don't error when there is no parent ([#4617](#4617)) ([a005703](a005703)) - **locale:** fix typos in japanese (ja) locale ([#4856](#4856)) ([3462cc5](3462cc5)) - **locale:** fixed typos in german (DE) locale ([#4631](#4631)) ([b7736de](b7736de)) - **locale:** proofread and updated de.json ([#4643](#4643)) ([8060ada](8060ada)) - **meta-viewport:** lower impact to moderate ([#4887](#4887)) ([2f32aa5](2f32aa5)), closes [#4714](#4714) - **no-autoplay-audio:** don't timeout for preload=none media elements ([#4684](#4684)) ([cdc871e](cdc871e)) - **performanceTimer:** throwing in axe catch clause ([#4852](#4852)) ([a4ade04](a4ade04)), closes [/github.com/dequelabs/axe-core/blob/e7dae4ec48cbfef74de9f833fdcfb178c1002985/lib/core/base/rule.js#L297-L300](https://github.com/dequelabs//github.com/dequelabs/axe-core/blob/e7dae4ec48cbfef74de9f833fdcfb178c1002985/lib/core/base/rule.js/issues/L297-L300) - **performanceTimer:** work in frames ([#4834](#4834)) ([d7dfebc](d7dfebc)) - **rules:** Change "alternate text" to "alternative text" ([#4582](#4582)) ([b03ada3](b03ada3)) - **target-size:** do not treat focusable tabpanels as targets ([#4702](#4702)) ([60d11f2](60d11f2)), closes [#4421](#4421) [#4701](#4701) - **type:** correct RuleError type ([#4893](#4893)) ([d1aa8e2](d1aa8e2)) - **types:** correct raw types ([#4903](#4903)) ([3eade11](3eade11)) This PR was opened by a robot 🤖 🎉
This PR implements hooks for customizing how
DqElements are serialized.Opening this as a draft for @WilcoFiers to pick up since I wasn't able to get it finished before heading off. The implementation is complete and it's passing integration tests (both old and new), but it still needs unit test updates (both to test the changes and also to fix up existing tests that relied on result nodes sometimes being
DqElements).After this PR, the only usage of
DqElementwithinaxe-coreitself is always of formnew DqElement(node, options).toJSON(), ie, serialized immediately after construction. I think this suggests that it'd be a better design to drop the class entirely and just have aserializeVirtualNodefunction instead that isn't unnecessarily stateful, but that's likely too breaking. We might consider markingDqElementas deprecated, though, to try to mitigate folks getting confused and thinking that they should be using it.Closes: #3471