diff --git a/client/modules/IDE/components/CopyableInput.jsx b/client/modules/IDE/components/CopyableInput/CopyableInput.jsx
similarity index 100%
rename from client/modules/IDE/components/CopyableInput.jsx
rename to client/modules/IDE/components/CopyableInput/CopyableInput.jsx
diff --git a/client/modules/IDE/components/CopyableInput.stories.jsx b/client/modules/IDE/components/CopyableInput/CopyableInput.stories.jsx
similarity index 100%
rename from client/modules/IDE/components/CopyableInput.stories.jsx
rename to client/modules/IDE/components/CopyableInput/CopyableInput.stories.jsx
diff --git a/client/modules/IDE/components/CopyableInput/CopyableInput.test.jsx b/client/modules/IDE/components/CopyableInput/CopyableInput.test.jsx
new file mode 100644
index 0000000000..df9b1306b9
--- /dev/null
+++ b/client/modules/IDE/components/CopyableInput/CopyableInput.test.jsx
@@ -0,0 +1,235 @@
+import React from 'react';
+import { render, screen, fireEvent, act } from '@testing-library/react';
+import Clipboard from 'clipboard';
+import CopyableInput from './CopyableInput';
+
+// Mock clipboard.js
+jest.mock('clipboard');
+
+// Mock react-i18next
+jest.mock('react-i18next', () => ({
+ useTranslation: () => ({
+ t: (key) => key
+ })
+}));
+
+// Mock ShareIcon SVG — must be a function, not a plain object
+jest.mock('../../../images/share.svg', () => {
+ const ShareIcon = () => ;
+ return ShareIcon;
+});
+
+describe('CopyableInput', () => {
+ const defaultProps = {
+ label: 'Test Label',
+ value: 'https://example.com'
+ };
+
+ let mockClipboardInstance;
+
+ beforeEach(() => {
+ mockClipboardInstance = {
+ on: jest.fn(),
+ destroy: jest.fn()
+ };
+ Clipboard.mockImplementation(() => mockClipboardInstance);
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ // ─── Rendering ────────────────────────────────────────────────────────────
+
+ describe('rendering', () => {
+ it('renders without crashing', () => {
+ render();
+ expect(screen.getByRole('textbox')).toBeInTheDocument();
+ });
+
+ it('renders the label text', () => {
+ render();
+ expect(screen.getByText('Test Label')).toBeInTheDocument();
+ });
+
+ it('renders the input with the correct value', () => {
+ render();
+ expect(screen.getByRole('textbox')).toHaveValue('https://example.com');
+ });
+
+ it('renders the input as readOnly', () => {
+ render();
+ expect(screen.getByRole('textbox')).toHaveAttribute('readonly');
+ });
+
+ it('associates label with input via htmlFor', () => {
+ render();
+ const input = screen.getByRole('textbox');
+ const label = screen.getByText('Test Label').closest('label');
+ expect(label).toHaveAttribute('for', input.id);
+ });
+
+ it('does not render the preview link by default', () => {
+ render();
+ expect(screen.queryByRole('link')).not.toBeInTheDocument();
+ });
+
+ it('renders the preview link when hasPreviewLink is true', () => {
+ render();
+ const link = screen.getByRole('link');
+ expect(link).toBeInTheDocument();
+ expect(link).toHaveAttribute('href', defaultProps.value);
+ });
+
+ it('opens the preview link in a new tab', () => {
+ render();
+ const link = screen.getByRole('link');
+ expect(link).toHaveAttribute('target', '_blank');
+ expect(link).toHaveAttribute('rel', 'noopener noreferrer');
+ });
+ });
+
+ // ─── CSS Classes ──────────────────────────────────────────────────────────
+
+ describe('CSS classes', () => {
+ it('applies the base copyable-input class', () => {
+ const { container } = render();
+ expect(container.firstChild).toHaveClass('copyable-input');
+ });
+
+ it('does not apply the --with-preview modifier by default', () => {
+ const { container } = render();
+ expect(container.firstChild).not.toHaveClass(
+ 'copyable-input--with-preview'
+ );
+ });
+
+ it('applies the --with-preview modifier when hasPreviewLink is true', () => {
+ const { container } = render(
+
+ );
+ expect(container.firstChild).toHaveClass('copyable-input--with-preview');
+ });
+
+ it('does not apply tooltip class initially', () => {
+ render();
+ const valueContainer = screen
+ .getByRole('textbox')
+ .closest('.copyable-input__value-container');
+ expect(valueContainer).not.toHaveClass('tooltipped');
+ expect(valueContainer).not.toHaveClass('tooltipped-n');
+ });
+ });
+
+ // ─── Clipboard Integration ────────────────────────────────────────────────
+
+ describe('clipboard integration', () => {
+ it('initialises Clipboard on mount', () => {
+ render();
+ expect(Clipboard).toHaveBeenCalledTimes(1);
+ });
+
+ it('passes the input element as the clipboard target', () => {
+ render();
+ const [, options] = Clipboard.mock.calls[0];
+ const input = screen.getByRole('textbox');
+ expect(options.target()).toBe(input);
+ });
+
+ it('registers a success handler on the clipboard instance', () => {
+ render();
+ expect(mockClipboardInstance.on).toHaveBeenCalledWith(
+ 'success',
+ expect.any(Function)
+ );
+ });
+
+ it('shows the tooltip after a successful copy', () => {
+ render();
+
+ const successCallback = mockClipboardInstance.on.mock.calls.find(
+ ([event]) => event === 'success'
+ )[1];
+
+ act(() => {
+ successCallback();
+ });
+
+ const valueContainer = screen
+ .getByRole('textbox')
+ .closest('.copyable-input__value-container');
+ expect(valueContainer).toHaveClass('tooltipped');
+ expect(valueContainer).toHaveClass('tooltipped-n');
+ });
+
+ it('destroys the clipboard instance on unmount', () => {
+ const { unmount } = render();
+ unmount();
+ expect(mockClipboardInstance.destroy).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ // ─── Tooltip / Mouse Interactions ─────────────────────────────────────────
+
+ describe('tooltip behaviour', () => {
+ it('hides the tooltip on mouse leave after a copy', () => {
+ render();
+
+ const successCallback = mockClipboardInstance.on.mock.calls.find(
+ ([event]) => event === 'success'
+ )[1];
+
+ act(() => {
+ successCallback();
+ });
+
+ const valueContainer = screen
+ .getByRole('textbox')
+ .closest('.copyable-input__value-container');
+
+ expect(valueContainer).toHaveClass('tooltipped');
+
+ fireEvent.mouseLeave(valueContainer);
+
+ expect(valueContainer).not.toHaveClass('tooltipped');
+ expect(valueContainer).not.toHaveClass('tooltipped-n');
+ });
+
+ it('sets the aria-label on the value container', () => {
+ render();
+ const valueContainer = screen
+ .getByRole('textbox')
+ .closest('.copyable-input__value-container');
+ expect(valueContainer).toHaveAttribute(
+ 'aria-label',
+ 'CopyableInput.CopiedARIA'
+ );
+ });
+ });
+
+ // ─── Accessibility ────────────────────────────────────────────────────────
+
+ describe('accessibility', () => {
+ it('renders the preview link with an aria-label when hasPreviewLink is true', () => {
+ render();
+ const link = screen.getByRole('link');
+ expect(link).toHaveAttribute('aria-label', 'CopyableInput.CopiedARIA');
+ });
+ });
+
+ // ─── Props ────────────────────────────────────────────────────────────────
+
+ describe('prop handling', () => {
+ it('updates the displayed value when the value prop changes', () => {
+ const { rerender } = render();
+ rerender();
+ expect(screen.getByRole('textbox')).toHaveValue('https://updated.com');
+ });
+
+ it('reflects a new label in the DOM when the label prop changes', () => {
+ const { rerender } = render();
+ rerender();
+ expect(screen.getByText('New Label')).toBeInTheDocument();
+ });
+ });
+});
diff --git a/client/modules/IDE/components/CopyableInput/index.js b/client/modules/IDE/components/CopyableInput/index.js
new file mode 100644
index 0000000000..941944273a
--- /dev/null
+++ b/client/modules/IDE/components/CopyableInput/index.js
@@ -0,0 +1 @@
+export { default } from './CopyableInput';
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 7866a79d9e..e2dd87940a 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22,6 +22,7 @@
"@redux-devtools/dock-monitor": "^3.0.1",
"@redux-devtools/log-monitor": "^4.0.2",
"@reduxjs/toolkit": "^1.9.3",
+ "@testing-library/user-event": "^14.6.1",
"@types/express": "^5.0.3",
"acorn": "^8.14.1",
"acorn-walk": "^8.3.4",
@@ -15102,7 +15103,6 @@
"version": "8.11.1",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.11.1.tgz",
"integrity": "sha512-3KQDyx9r0RKYailW2MiYrSSKEfH0GTkI51UGEvJenvcoDoeRYs0PZpi2SXqtnMClQvCqdtTTpOfFETDTVADpAg==",
- "dev": true,
"dependencies": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5",
@@ -15121,7 +15121,6 @@
"version": "27.2.5",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-27.2.5.tgz",
"integrity": "sha512-nmuM4VuDtCZcY+eTpw+0nvstwReMsjPoj7ZR80/BbixulhLaiX+fbv8oeLW8WZlJMcsGQsTmMKT/iTZu1Uy/lQ==",
- "dev": true,
"dependencies": {
"@types/istanbul-lib-coverage": "^2.0.0",
"@types/istanbul-reports": "^3.0.0",
@@ -15137,7 +15136,6 @@
"version": "16.0.4",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
"integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==",
- "dev": true,
"dependencies": {
"@types/yargs-parser": "*"
}
@@ -15146,7 +15144,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@@ -15161,7 +15158,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz",
"integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==",
- "dev": true,
"engines": {
"node": ">=6.0"
}
@@ -15170,7 +15166,6 @@
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
"dependencies": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -15186,7 +15181,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
"dependencies": {
"color-name": "~1.1.4"
},
@@ -15197,14 +15191,12 @@
"node_modules/@testing-library/dom/node_modules/color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"node_modules/@testing-library/dom/node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true,
"engines": {
"node": ">=8"
}
@@ -15213,7 +15205,6 @@
"version": "27.3.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.3.1.tgz",
"integrity": "sha512-DR/c+pvFc52nLimLROYjnXPtolawm+uWDxr4FjuLDLUn+ktWnSN851KoHwHzzqq6rfCOjkzN8FLgDrSub6UDuA==",
- "dev": true,
"dependencies": {
"@jest/types": "^27.2.5",
"ansi-regex": "^5.0.1",
@@ -15228,7 +15219,6 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
- "dev": true,
"engines": {
"node": ">=10"
},
@@ -15239,14 +15229,12 @@
"node_modules/@testing-library/dom/node_modules/react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
},
"node_modules/@testing-library/dom/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
"dependencies": {
"has-flag": "^4.0.0"
},
@@ -15364,7 +15352,6 @@
"version": "14.6.1",
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz",
"integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=12",
@@ -15386,8 +15373,7 @@
"node_modules/@types/aria-query": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz",
- "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==",
- "dev": true
+ "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig=="
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
@@ -15620,14 +15606,12 @@
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
- "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==",
- "dev": true
+ "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw=="
},
"node_modules/@types/istanbul-lib-report": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
"integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
- "dev": true,
"dependencies": {
"@types/istanbul-lib-coverage": "*"
}
@@ -15636,7 +15620,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
"integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==",
- "dev": true,
"dependencies": {
"@types/istanbul-lib-report": "*"
}
@@ -16080,8 +16063,7 @@
"node_modules/@types/yargs-parser": {
"version": "20.2.1",
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz",
- "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==",
- "dev": true
+ "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw=="
},
"node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.62.0",
@@ -21209,8 +21191,7 @@
"node_modules/dom-accessibility-api": {
"version": "0.5.10",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.10.tgz",
- "integrity": "sha512-Xu9mD0UjrJisTmv7lmVSDMagQcU9R5hwAbxsaAE/35XPnPLJobbuREfV/rraiSaEj/UOvgrzQs66zyTWTlyd+g==",
- "dev": true
+ "integrity": "sha512-Xu9mD0UjrJisTmv7lmVSDMagQcU9R5hwAbxsaAE/35XPnPLJobbuREfV/rraiSaEj/UOvgrzQs66zyTWTlyd+g=="
},
"node_modules/dom-converter": {
"version": "0.2.0",
@@ -28758,7 +28739,6 @@
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
- "dev": true,
"bin": {
"lz-string": "bin/bin.js"
}
@@ -49881,7 +49861,6 @@
"version": "8.11.1",
"resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.11.1.tgz",
"integrity": "sha512-3KQDyx9r0RKYailW2MiYrSSKEfH0GTkI51UGEvJenvcoDoeRYs0PZpi2SXqtnMClQvCqdtTTpOfFETDTVADpAg==",
- "dev": true,
"requires": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5",
@@ -49897,7 +49876,6 @@
"version": "27.2.5",
"resolved": "https://registry.npmjs.org/@jest/types/-/types-27.2.5.tgz",
"integrity": "sha512-nmuM4VuDtCZcY+eTpw+0nvstwReMsjPoj7ZR80/BbixulhLaiX+fbv8oeLW8WZlJMcsGQsTmMKT/iTZu1Uy/lQ==",
- "dev": true,
"requires": {
"@types/istanbul-lib-coverage": "^2.0.0",
"@types/istanbul-reports": "^3.0.0",
@@ -49910,7 +49888,6 @@
"version": "16.0.4",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-16.0.4.tgz",
"integrity": "sha512-T8Yc9wt/5LbJyCaLiHPReJa0kApcIgJ7Bn735GjItUfh08Z1pJvu8QZqb9s+mMvKV6WUQRV7K2R46YbjMXTTJw==",
- "dev": true,
"requires": {
"@types/yargs-parser": "*"
}
@@ -49919,7 +49896,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
"requires": {
"color-convert": "^2.0.1"
}
@@ -49927,14 +49903,12 @@
"aria-query": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.0.0.tgz",
- "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg==",
- "dev": true
+ "integrity": "sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg=="
},
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
"integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
- "dev": true,
"requires": {
"ansi-styles": "^4.1.0",
"supports-color": "^7.1.0"
@@ -49944,7 +49918,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
"requires": {
"color-name": "~1.1.4"
}
@@ -49952,20 +49925,17 @@
"color-name": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
},
"has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
- "dev": true
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
},
"pretty-format": {
"version": "27.3.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.3.1.tgz",
"integrity": "sha512-DR/c+pvFc52nLimLROYjnXPtolawm+uWDxr4FjuLDLUn+ktWnSN851KoHwHzzqq6rfCOjkzN8FLgDrSub6UDuA==",
- "dev": true,
"requires": {
"@jest/types": "^27.2.5",
"ansi-regex": "^5.0.1",
@@ -49976,22 +49946,19 @@
"ansi-styles": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
- "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
- "dev": true
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="
}
}
},
"react-is": {
"version": "17.0.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
- "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
- "dev": true
+ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
- "dev": true,
"requires": {
"has-flag": "^4.0.0"
}
@@ -50080,7 +50047,6 @@
"version": "14.6.1",
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz",
"integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==",
- "dev": true,
"requires": {}
},
"@trysound/sax": {
@@ -50092,8 +50058,7 @@
"@types/aria-query": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.2.tgz",
- "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig==",
- "dev": true
+ "integrity": "sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig=="
},
"@types/babel__core": {
"version": "7.20.5",
@@ -50319,14 +50284,12 @@
"@types/istanbul-lib-coverage": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz",
- "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==",
- "dev": true
+ "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw=="
},
"@types/istanbul-lib-report": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz",
"integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==",
- "dev": true,
"requires": {
"@types/istanbul-lib-coverage": "*"
}
@@ -50335,7 +50298,6 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz",
"integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==",
- "dev": true,
"requires": {
"@types/istanbul-lib-report": "*"
}
@@ -50760,8 +50722,7 @@
"@types/yargs-parser": {
"version": "20.2.1",
"resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-20.2.1.tgz",
- "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw==",
- "dev": true
+ "integrity": "sha512-7tFImggNeNBVMsn0vLrpn1H1uPrUBdnARPTpZoitY37ZrdJREzf7I16tMrlK3hen349gr1NYh8CmZQa7CTG6Aw=="
},
"@typescript-eslint/eslint-plugin": {
"version": "5.62.0",
@@ -54421,8 +54382,7 @@
"dom-accessibility-api": {
"version": "0.5.10",
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.10.tgz",
- "integrity": "sha512-Xu9mD0UjrJisTmv7lmVSDMagQcU9R5hwAbxsaAE/35XPnPLJobbuREfV/rraiSaEj/UOvgrzQs66zyTWTlyd+g==",
- "dev": true
+ "integrity": "sha512-Xu9mD0UjrJisTmv7lmVSDMagQcU9R5hwAbxsaAE/35XPnPLJobbuREfV/rraiSaEj/UOvgrzQs66zyTWTlyd+g=="
},
"dom-converter": {
"version": "0.2.0",
@@ -59945,8 +59905,7 @@
"lz-string": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz",
- "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
- "dev": true
+ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="
},
"magic-string": {
"version": "0.30.10",
diff --git a/package.json b/package.json
index f0ef304515..1d61484d91 100644
--- a/package.json
+++ b/package.json
@@ -217,6 +217,7 @@
"@redux-devtools/dock-monitor": "^3.0.1",
"@redux-devtools/log-monitor": "^4.0.2",
"@reduxjs/toolkit": "^1.9.3",
+ "@testing-library/user-event": "^14.6.1",
"@types/express": "^5.0.3",
"acorn": "^8.14.1",
"acorn-walk": "^8.3.4",