-
Notifications
You must be signed in to change notification settings - Fork 339
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
Allow CharacterCount component to receive i18n config via JS #2887
Conversation
b6baf50
to
a4c3c0d
Compare
a4c3c0d
to
9217683
Compare
lib/dom-helpers.mjs
Outdated
@@ -0,0 +1,19 @@ | |||
/** | |||
* Creates an element with the given attributes |
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'm not 100% sure that we need this abstraction, and it could end up trying to do too much if we extend it to do everything we might want to do with a created element as the TODOs are hinting at.
Are we sure we want to go down this route rather than just using the native APIs directly in the tests?
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.
Issue created for discussion there: #2894
let component | ||
beforeAll(() => { | ||
// The component won't initialise if we don't pass it an element | ||
component = new CharacterCount(createElement('div')) |
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.
Slightly nervous about passing a div here, and imagine this could come back to haunt us in the future when e.g. we get rid of the init
function (which at the minute we're not calling, so a lot of the 'setup' for the component isn't happening).
Don't have any immediate suggestions for a fix, and this might just be something we have to revisit later… but it feels a bit fragile.
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.
Because we have the Puppeteer tests, I'm not too worried about mocking what the component runs onto and skipping its initialisation.
If anything, having these tests only depend on the bare minimum that the feature needs (one element and no init
call) can help us ensure we do update tests: if the formatting starts to depend on more than what's mocked, these tests will fail and force us to update them or to better isolate that method, and maybe prompt us to add new cases around the change that we may have missed through the end-to-end tests in Puppeteer (because we can't cover all combinations of everything there, while we can be more thorough in unit tests which run with less overhead).
Is it the extra updates that worry you?
src/govuk/components/character-count/character-count.unit.test.mjs
Outdated
Show resolved
Hide resolved
src/govuk/components/character-count/character-count.unit.test.mjs
Outdated
Show resolved
Hide resolved
lib/dom-helpers.mjs
Outdated
* @returns HTMLElement | ||
*/ | ||
export function createElement (tagName, attributes = {}) { | ||
const el = document.createElement(tagName) |
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 flagged in Slack, although document
seems to be part of the default globals for Standard, we might want to explicitly configure eslint so that it knows this is file is in the context of a browser / jsdom (for example by adding another override for this file in .eslintrc.js
)
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.
If we go down the route of finely tracking which environment which file is in, I feel it's something that should happen across the whole codebase in a separate PR, otherwise we'll have parts of the code still able to access the browser globals when they shouldn't.
That said, as I mentioned on Slack, I'm not sure it's super useful to track browser globals with that granularity:
- our project does run in the browser after all
- any code that doesn't run in the browser will either run locally or on CI first and we can uncover access to non existing globals there.
87932f1
to
fe227d8
Compare
fe227d8
to
31a3213
Compare
31a3213
to
cead23d
Compare
1ef30e0
to
1c74599
Compare
@36degrees Made the updates based on your last feedback. Happy to discuss test and linting concerns further but I feel they're two things that could be fixed in future PRs rather than completely blocking that one from moving forward? |
@romaricpascal I've found it helps to ask if anything has changed "for the worse" in the PR If it's something we agree we can live with temporarily, fine. Otherwise if the changes have carried on using existing conventions an techniques, code style etc, improvements can come later |
@romaricpascal I'll add my thoughts for the future here Looking at the dom-helpers.js module (similar to puppeteer-helpers.js that I added) perhaps we should extend the Jest environments instead, not yet but soon? Otherwise could it grow into a "mini jQuery"? We already use cheerio for this elsewhere. Faster to use a library that to write our own utilities. Have you thought about adding these at environment level? Like the Puppeteer tests having the Jest environmentsSince Jest doesn't implement
|
Oh, that's a good one, I'll keep it in mind. If things are moving "for the better", we can always move them further "for the better" in future PRs indeed.
I think that words what I'm trying to get: a confirmation that we'd be fine shipping as is and revisiting in the future if we want to amend tests/linting
I have a slightly different direction in mind for these helpers, where we could also use them to create elements in the components themselves. Unlike the querying, the DOM API is very verbose when it comes to creating elements with attributes and children and I think we'd gain some readability with a little abstraction there. I've opened an issue to discuss there: #2894.
Yeah, that's something we'd need to keep an eye on if we move these helpers further.
Cheerio didn't quite fit the bill there as we needed to get an instance of
Wasn't aware of the lack of require caching in Jest, cheers for pointing it out 😄 I'm wondering how much of a bottleneck it is vs how much we'd lose of which files rely on which helpers that |
Ahh fair enough, I thought Cheerio had jQuery's Should dom-helpers.js be moved inside |
It does, but the returned object doesn't have an equivalent of jQuery's
Not yet, it is deliberately in |
See what @36degrees thinks? We should do the hard work (+ tests) via a nice neat DOM helpers file, but only if it was going in Until that happens, if it's just for some some quick ad-hoc test utilities we can use shorthand? Need to be aware of the subtle differences between properties/attributes, but these do the job: Adding const $localised = Object.assign(document.createElement('div'), {
lang: 'de'
}) Adding content to const $content = Object.assign(document.createElement('div'), {
innerHtml: 'Hello World'
}) Finding closest $closest = $element.closest('[lang]')?.getAttribute('lang') |
|
||
const component = new CharacterCount($div) | ||
|
||
expect(component.formatCountMessage(10000, 'words')).toEqual('You have 10.000 words remaining') |
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.
Super minor one, but any thoughts on logically grouping what the test is doing?
const $parent = document.createElement('div')
const $div = document.createElement('div')
$parent.setAttribute('lang', 'de')
$parent.appendChild($div)
const component = new CharacterCount($div)
const countMessage = component.formatCountMessage(10000, 'words')
expect(countMessage).toEqual('You have 10.000 words remaining')
- Create things
- Customise things
- Run some code
- Run assertion
Helps with readability slightly, not a blocker though
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.
Some minor comments, but looks fab so approving
I'll leave it with you 😊
Update: Oh and the conflict, quick rebase and we'll see if everything passes again?
Introduce a function focused on formatting the message to help with testing
Provided by the I18n object
Commit is a bit bulky for what sounds a simple thing, but it's mostly behind the scene logistics: - extracting the helper for creating dom elements in its own file - introducing a function to get the value of an attribute from the closest parent with it
Make the HTML structure more apparent than creating a series of elements manually
Updates the testing to use the JavaScript configuration
82bdf28
to
3a42be1
Compare
Waiting for further discussion about abstraction in #2894
Co-authored-by: Colin Rotherham <[email protected]>
727dbdb
to
a5ed606
Compare
Builds upon the configuration work for the CharacterCount component to allow translating the message displaying the count.
To avoid adding another flurry of Puppeteer tests, the PR starts with a little refactoring to isolate the formatting of the message so it can be unit tested. The Puppeteer tests remain in place to keep testing that the right thing appears based on user interactions.
Closes #2805, and closes #2568 as well, and closes #2701 also.