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

feat(common): Remove uuid and update unique id generation #1408

Merged

Conversation

NicholasBoll
Copy link
Member

@NicholasBoll NicholasBoll commented Jan 5, 2022

Summary

Our current id generation has issues.

  • It generates very long ids that add noise to the page
  • It is possible to generate a blank id
  • Id generation is slow
  • Snapshot tests require mocking the uuid module

Fixes: #1311, #1106, #664, #602

category

Release Note

NOTE for jest snapshots: This change removes the uuid package and instead will generate a one-time client seed and then create auto-incrementing ids. This change will not break UI or automated UI tests. It will break snapshot tests however. Previously, the only way to get stable ids for snapshot tests was to mock the uuid module. This was an implementation detail. To make snapshots work again, add the following to your jest setup file:

import {setUniqueSeed, resetUniqueIdCount} from '@workday/canvas-kit-react/common';

beforeEach(() => {
  setUniqueSeed('a'); // force set the seed
  resetUniqueIdCount(); // reset the unique id count
});

This will ensure each Jest snapshot has ids that look like a0 and a1 and will be the same every time the snapshot is run. Do not use these methods in production though - it may lead to inaccessible applications due to IDREF collisions.


Additional References

Test of different ID generation methods https://codesandbox.io/s/tkod6

Remaining dependencies on older versions of uuid:

  • request - this is a deprecated lib that we use for scripts
  • node-sass - We're using v4. Newest is v7. This PR removed request which is using uuid@3, but hasn't been released yet: Use make-fetch-happen instead of request sass/node-sass#3193. Currently blocked. sass-loader that is compatible with node-sass@7 requires webpack@5 which Storybook doesn't currently support.
  • jest-unit - We're using v10. Current version is v13
  • lerna - We're using v3. Current version is v4
  • react-docgen-typescript-loader - We're on v3.3.0. Latest is v3.7.2
  • storybook/addon-docs - Oh, Storybook...

@@ -37,7 +37,7 @@ describe('Combobox', () => {
});

it('should not show menu', () => {
cy.findByRole('listbox').should('not.be.visible');
cy.findByRole('listbox').should('not.exist');
Copy link
Member Author

Choose a reason for hiding this comment

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

All these were due to a breaking change in Cypress v6.

This "fix" did find legitimate issues with our tests, but I feel like we've lost some semantics. I want to know if the element is no longer accessible to the user. Whether it is still in the DOM or just hidden is an implementation detail. Perhaps I'll make an be.inaccessible assertion that does this.

@@ -155,14 +155,14 @@ describe('Tooltip', () => {
});

it('the span element should not have an aria-describedby attribute', () => {
cy.get('button').should('not.have.attr', 'aria-describedby');
cy.get('span').should('not.have.attr', 'aria-describedby');
Copy link
Member Author

Choose a reason for hiding this comment

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

This was caught by the change in Cypress v6. Before, not.have.attr would pass even if cy.get('button') never actually matched anything. This was a typo from long ago that Cypress was perfectly happy to incorrectly pass.

});

context('when the "Delete" button is hovered', () => {
context('when the "Some Text" text is hovered', () => {
Copy link
Member Author

Choose a reason for hiding this comment

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

This was a copy/paste error. The tooltip isn't from a delete button, but a span with some text.

if (typeof url === 'object') {
// eslint-disable-next-line no-param-reassign
url = options.url;
Cypress.Commands.overwrite('visit', (originalFn, urlOrOptions, inputOptions = {}) => {
Copy link
Member Author

Choose a reason for hiding this comment

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

Changes in this file were from Cypress adding type defintions to Cypress.Commands.add and Cypress.Commands.overwrite in Cypress v9. In most cases, this is an improvement. In the case of commands that use Typescript overrides, this causes problems. I've rearranged command overrides and added some any in cases where the Cypress types introduce problems that cannot be resolved.

@cypress
Copy link

cypress bot commented Jan 5, 2022



Test summary

666 0 1 0Flakiness 0


Run details

Project canvas-kit
Status Passed
Commit d8b12e9 ℹ️
Started Jan 7, 2022 10:53 PM
Ended Jan 7, 2022 10:59 PM
Duration 05:26 💡
OS Linux Ubuntu - 20.04
Browser Electron 94

View run in Cypress Dashboard ➡️


This comment has been generated by cypress-bot as a result of this project's GitHub integration settings. You can manage this integration in this project's settings in the Cypress Dashboard

@@ -1,5 +1,5 @@
declare namespace Cypress {
interface Chainable {
tab(options?: Partial<{shift: Boolean}>): Chainable;
interface Chainable<Subject = any> {
Copy link
Member Author

Choose a reason for hiding this comment

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

This was the correct typing all along

Comment on lines +10 to +16
const resizeObserverLoopErrRe = /^[^(ResizeObserver loop limit exceeded)]/;
Cypress.on('uncaught:exception', err => {
/* returning false here prevents Cypress from failing the test */
if (resizeObserverLoopErrRe.test(err.message)) {
return false;
}
});
Copy link
Member Author

Choose a reason for hiding this comment

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

The ResizeObserver loop limit exceeded error started happening after upgrading Cypress from 5.6.0 to 9.2.0. I'm not sure which Cypress version introduced the error. Perhaps later we can find out.

Comment on lines +20 to +23
beforeEach(() => {
setUniqueSeed('a');
resetUniqueIdCount();
});
Copy link
Member Author

Choose a reason for hiding this comment

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

This isn't actually needed for our tests, but it is nice to have something to point to as an example for teams that need this setup.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm sure teams are gonna wanna use this, nice addition

Copy link
Contributor

Choose a reason for hiding this comment

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

Should something like this go into the testing docs for reference?

Copy link
Contributor

Choose a reason for hiding this comment

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

nvm you documented this pretty well in the readme

@@ -6,3 +6,5 @@ export * from './select';
export * from './side-panel';
export * from './text-area';
export * from './text-input';

/// <reference types="@types/node" />
Copy link
Member Author

Choose a reason for hiding this comment

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

This removes Typescript errors complaining that process doesn't exist. uuid added @types/node to the global namespace for us.

let seed = Math.random()
.toString(36)
.slice(2)
.replace(/[0-9]*/, '')
Copy link
Contributor

Choose a reason for hiding this comment

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

did you want to add the - here? I remember the code sandbox you added it.

Copy link
Member Author

Choose a reason for hiding this comment

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

I added it to the uuid generation. Base 36 doesn't include dashes.

Copy link
Contributor

@mannycarrera4 mannycarrera4 left a comment

Choose a reason for hiding this comment

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

Looks good to me just some minor comments/ questions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants