@kiva/kv-components 1.4.4 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{.eslintrc.js → .eslintrc.cjs} +1 -1
- package/CHANGELOG.md +75 -0
- package/README.md +2 -2
- package/__mocks__/ResizeObserver.js +13 -0
- package/package.json +34 -20
- package/{postcss.config.js → postcss.config.cjs} +2 -2
- package/{tailwind.config.js → tailwind.config.cjs} +10 -13
- package/tests/unit/jest-setup.js +5 -0
- package/tests/unit/specs/components/KvButton.spec.js +14 -25
- package/tests/unit/specs/components/KvCarousel.spec.js +11 -0
- package/tests/unit/specs/components/KvCheckbox.spec.js +73 -14
- package/tests/unit/specs/components/KvLightbox.spec.js +14 -0
- package/tests/unit/specs/components/KvProgressBar.spec.js +11 -0
- package/tests/unit/specs/components/KvRadio.spec.js +94 -5
- package/tests/unit/specs/components/KvSelect.spec.js +113 -0
- package/tests/unit/specs/components/KvSwitch.spec.js +92 -33
- package/tests/unit/specs/components/KvTabPanel.spec.js +11 -0
- package/tests/unit/specs/components/KvTabs.spec.js +99 -0
- package/tests/unit/specs/components/KvTextInput.spec.js +86 -9
- package/tests/unit/specs/components/KvTextLink.spec.js +16 -24
- package/tests/unit/specs/components/KvToast.spec.js +11 -0
- package/tests/unit/utils/addVueRouter.js +24 -0
- package/utils/attrs.js +62 -0
- package/utils/{themeUtils.js → themeUtils.cjs} +0 -0
- package/vue/.storybook/main.cjs +33 -0
- package/vue/.storybook/preview.js +6 -1
- package/vue/KvButton.vue +80 -53
- package/vue/KvCarousel.vue +142 -106
- package/vue/KvCheckbox.vue +86 -60
- package/vue/KvContentfulImg.vue +45 -34
- package/vue/KvLightbox.vue +108 -69
- package/vue/KvProgressBar.vue +33 -19
- package/vue/KvRadio.vue +72 -41
- package/vue/KvSelect.vue +46 -20
- package/vue/KvSwitch.vue +55 -33
- package/vue/KvTab.vue +49 -21
- package/vue/KvTabPanel.vue +26 -6
- package/vue/KvTabs.vue +70 -53
- package/vue/KvTextInput.vue +71 -48
- package/vue/KvTextLink.vue +42 -20
- package/vue/KvThemeProvider.vue +1 -1
- package/vue/KvToast.vue +53 -37
- package/vue/stories/KvCheckbox.stories.js +5 -5
- package/vue/stories/KvSwitch.stories.js +2 -2
- package/vue/stories/KvTabs.stories.js +8 -8
- package/vue/stories/KvTextInput.stories.js +1 -1
- package/vue/stories/KvThemeProvider.stories.js +1 -1
- package/vue/stories/KvToast.stories.js +3 -2
- package/vue/stories/StyleguidePrimitives.stories.js +10 -10
- package/.babelrc +0 -16
- package/jest.config.js +0 -36
- package/vue/.storybook/main.js +0 -36
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { render, fireEvent } from '@testing-library/vue';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { axe } from 'jest-axe';
|
|
4
|
+
import KvSelect from '../../../../vue/KvSelect.vue';
|
|
5
|
+
|
|
6
|
+
describe('KvSelect', () => {
|
|
7
|
+
it('renders with a role of "select"', () => {
|
|
8
|
+
const { getByRole } = render(KvSelect, {
|
|
9
|
+
slots: { default: 'Test Select' },
|
|
10
|
+
props: { id: 'test' },
|
|
11
|
+
});
|
|
12
|
+
const selectEl = getByRole('combobox');
|
|
13
|
+
|
|
14
|
+
expect(selectEl).toBeDefined();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('works with v-model', async () => {
|
|
18
|
+
const TestComponent = {
|
|
19
|
+
template:
|
|
20
|
+
`<div>
|
|
21
|
+
<label for="test-select">Test select</label>
|
|
22
|
+
<KvSelect id="test-select" v-model="selectValue">
|
|
23
|
+
<option value="a">A</option>
|
|
24
|
+
<option value="b">B</option>
|
|
25
|
+
<option value="c">C</option>
|
|
26
|
+
</KvSelect>
|
|
27
|
+
<button @click="selectValue = 'b'">reset</button>
|
|
28
|
+
<span>The select value is {{ selectValue }}</span>
|
|
29
|
+
</div>`,
|
|
30
|
+
components: { KvSelect },
|
|
31
|
+
data: () => ({ selectValue: 'b' }),
|
|
32
|
+
};
|
|
33
|
+
const { getByRole, getByText } = render(TestComponent);
|
|
34
|
+
const selectEl = getByRole('combobox');
|
|
35
|
+
|
|
36
|
+
// Check that the value is 'b' initially
|
|
37
|
+
expect(getByText('The select value is b')).toBeDefined();
|
|
38
|
+
expect(selectEl.value).toEqual('b');
|
|
39
|
+
|
|
40
|
+
// Click the switch and expect the value to be `true` now
|
|
41
|
+
await userEvent.selectOptions(selectEl, 'C');
|
|
42
|
+
expect(getByText('The select value is c')).toBeDefined();
|
|
43
|
+
expect(selectEl.value).toEqual('c');
|
|
44
|
+
|
|
45
|
+
// Click the reset button and expect the value to be `false` again
|
|
46
|
+
await fireEvent.click(getByText('reset'));
|
|
47
|
+
expect(getByText('The select value is b')).toBeDefined();
|
|
48
|
+
expect(selectEl.value).toEqual('b');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('applies parent event listeners to the input element', async () => {
|
|
52
|
+
const onChange = jest.fn();
|
|
53
|
+
const TestComponent = {
|
|
54
|
+
template:
|
|
55
|
+
`<KvSelect id="test" @change="onChange">
|
|
56
|
+
<option>A</option>
|
|
57
|
+
<option>B</option>
|
|
58
|
+
</KvSelect>`,
|
|
59
|
+
components: { KvSelect },
|
|
60
|
+
methods: { onChange },
|
|
61
|
+
};
|
|
62
|
+
const { getByRole } = render(TestComponent);
|
|
63
|
+
|
|
64
|
+
const selectEl = getByRole('combobox');
|
|
65
|
+
await userEvent.selectOptions(selectEl, 'B');
|
|
66
|
+
expect(onChange.mock.calls.length).toBe(1);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('applies parent attributes to the input element', async () => {
|
|
70
|
+
const TestComponent = {
|
|
71
|
+
template: '<KvSelect id="test" name="test-select"></KvSelect>',
|
|
72
|
+
components: { KvSelect },
|
|
73
|
+
};
|
|
74
|
+
const { getByRole } = render(TestComponent);
|
|
75
|
+
|
|
76
|
+
const selectEl = getByRole('combobox');
|
|
77
|
+
expect(selectEl.name).toBe('test-select');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('applies parent styles to the root element', async () => {
|
|
81
|
+
const TestComponent = {
|
|
82
|
+
template: '<KvSelect id="test" style="padding-top:1234px"></KvSelect>',
|
|
83
|
+
components: { KvSelect },
|
|
84
|
+
};
|
|
85
|
+
const { container } = render(TestComponent);
|
|
86
|
+
|
|
87
|
+
expect(container.firstChild.style.paddingTop).toEqual('1234px');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('applies parent classes to the root element', async () => {
|
|
91
|
+
const TestComponent = {
|
|
92
|
+
template: '<KvSelect id="test" class="test-class"></KvSelect>',
|
|
93
|
+
components: { KvSelect },
|
|
94
|
+
};
|
|
95
|
+
const { container } = render(TestComponent);
|
|
96
|
+
|
|
97
|
+
expect(container.firstChild.classList).toContain('test-class');
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('has no automated accessibility violations', async () => {
|
|
101
|
+
const TestComponent = {
|
|
102
|
+
template:
|
|
103
|
+
`<div>
|
|
104
|
+
<label for="test-select">Test select</label>
|
|
105
|
+
<KvSelect id="test-select">options</KvSelect>
|
|
106
|
+
</div>`,
|
|
107
|
+
components: { KvSelect },
|
|
108
|
+
};
|
|
109
|
+
const { container } = render(TestComponent);
|
|
110
|
+
const results = await axe(container);
|
|
111
|
+
expect(results).toHaveNoViolations();
|
|
112
|
+
});
|
|
113
|
+
});
|
|
@@ -1,60 +1,119 @@
|
|
|
1
|
-
import { render } from '@testing-library/vue';
|
|
2
|
-
import { axe
|
|
1
|
+
import { render, fireEvent } from '@testing-library/vue';
|
|
2
|
+
import { axe } from 'jest-axe';
|
|
3
3
|
import KvSwitch from '../../../../vue/KvSwitch.vue';
|
|
4
4
|
|
|
5
|
-
expect.extend(toHaveNoViolations);
|
|
6
|
-
|
|
7
5
|
describe('KvSwitch', () => {
|
|
6
|
+
const renderTestSwitch = (options) => render(KvSwitch, {
|
|
7
|
+
slots: { default: 'Test Switch' },
|
|
8
|
+
...options,
|
|
9
|
+
});
|
|
10
|
+
|
|
8
11
|
it('renders with a role of "switch"', () => {
|
|
9
|
-
const { getByRole } =
|
|
10
|
-
slots: { default: 'Test Switch' },
|
|
11
|
-
});
|
|
12
|
+
const { getByRole } = renderTestSwitch();
|
|
12
13
|
const switchEl = getByRole('switch');
|
|
13
14
|
|
|
14
15
|
expect(switchEl).toBeDefined();
|
|
15
16
|
});
|
|
16
17
|
|
|
17
18
|
it('toggles the switch when the label is clicked', async () => {
|
|
18
|
-
const { getByLabelText } =
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const switchEl = getByLabelText('Test Switch');
|
|
19
|
+
const { getByText, getByLabelText } = renderTestSwitch();
|
|
20
|
+
const switchEl = getByText('Test Switch');
|
|
21
|
+
const switchInput = getByLabelText('Test Switch');
|
|
22
22
|
|
|
23
|
-
expect(
|
|
24
|
-
await
|
|
25
|
-
expect(
|
|
23
|
+
expect(switchInput.checked).toEqual(false);
|
|
24
|
+
await fireEvent.click(switchEl);
|
|
25
|
+
expect(switchInput.checked).toEqual(true);
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
it('can\'t be toggled when the disabled prop is true', async () => {
|
|
29
|
-
const { getByLabelText } =
|
|
29
|
+
const { getByText, getByLabelText } = renderTestSwitch({
|
|
30
30
|
props: { disabled: true },
|
|
31
|
-
slots: { default: 'Test Switch' },
|
|
32
31
|
});
|
|
33
|
-
const switchEl =
|
|
32
|
+
const switchEl = getByText('Test Switch');
|
|
33
|
+
const switchInput = getByLabelText('Test Switch');
|
|
34
34
|
|
|
35
|
-
expect(
|
|
36
|
-
await
|
|
37
|
-
expect(
|
|
35
|
+
expect(switchInput.checked).toEqual(false);
|
|
36
|
+
await fireEvent.click(switchEl);
|
|
37
|
+
expect(switchInput.checked).toEqual(false);
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
it('
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
it('works with v-model', async () => {
|
|
41
|
+
const TestComponent = {
|
|
42
|
+
template:
|
|
43
|
+
`<div>
|
|
44
|
+
<KvSwitch v-model="switchValue">Test Switch</KvSwitch>
|
|
45
|
+
<button @click="switchValue = false">reset</button>
|
|
46
|
+
<span>The switch value is {{ switchValue }}</span>
|
|
47
|
+
</div>`,
|
|
48
|
+
components: { KvSwitch },
|
|
49
|
+
data: () => ({ switchValue: false }),
|
|
50
|
+
};
|
|
51
|
+
const { getByLabelText, getByText } = render(TestComponent);
|
|
52
|
+
const switchEl = getByText('Test Switch');
|
|
53
|
+
const switchInput = getByLabelText('Test Switch');
|
|
45
54
|
|
|
46
|
-
|
|
47
|
-
expect(
|
|
55
|
+
// Check that the value is `false` initially
|
|
56
|
+
expect(getByText('The switch value is false')).toBeDefined();
|
|
57
|
+
expect(switchInput.checked).toEqual(false);
|
|
48
58
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
expect(
|
|
59
|
+
// Click the switch and expect the value to be `true` now
|
|
60
|
+
await fireEvent.click(switchEl);
|
|
61
|
+
expect(getByText('The switch value is true')).toBeDefined();
|
|
62
|
+
expect(switchInput.checked).toEqual(true);
|
|
63
|
+
|
|
64
|
+
// Click the reset button and expect the value to be `false` again
|
|
65
|
+
await fireEvent.click(getByText('reset'));
|
|
66
|
+
expect(getByText('The switch value is false')).toBeDefined();
|
|
67
|
+
expect(switchInput.checked).toEqual(false);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('applies parent event listeners to the input element', async () => {
|
|
71
|
+
const onInput = jest.fn();
|
|
72
|
+
const TestComponent = {
|
|
73
|
+
template: '<KvSwitch @input="onInput">Test Switch</KvSwitch>',
|
|
74
|
+
components: { KvSwitch },
|
|
75
|
+
methods: { onInput },
|
|
76
|
+
};
|
|
77
|
+
const { getByText } = render(TestComponent);
|
|
78
|
+
|
|
79
|
+
const switchEl = getByText('Test Switch');
|
|
80
|
+
await fireEvent.click(switchEl);
|
|
81
|
+
expect(onInput.mock.calls.length).toBe(1);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('applies parent attributes to the input element', async () => {
|
|
85
|
+
const TestComponent = {
|
|
86
|
+
template: '<KvSwitch name="test-switch">Test Switch</KvSwitch>',
|
|
87
|
+
components: { KvSwitch },
|
|
88
|
+
};
|
|
89
|
+
const { getByRole } = render(TestComponent);
|
|
90
|
+
|
|
91
|
+
const switchEl = getByRole('switch');
|
|
92
|
+
expect(switchEl.name).toBe('test-switch');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('applies parent styles to the root element', async () => {
|
|
96
|
+
const TestComponent = {
|
|
97
|
+
template: '<KvSwitch style="padding-top:1234px">Test Switch</KvSwitch>',
|
|
98
|
+
components: { KvSwitch },
|
|
99
|
+
};
|
|
100
|
+
const { container } = render(TestComponent);
|
|
101
|
+
|
|
102
|
+
expect(container.firstChild.style.paddingTop).toEqual('1234px');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('applies parent classes to the root element', async () => {
|
|
106
|
+
const TestComponent = {
|
|
107
|
+
template: '<KvSwitch class="test-class">Test Switch</KvSwitch>',
|
|
108
|
+
components: { KvSwitch },
|
|
109
|
+
};
|
|
110
|
+
const { container } = render(TestComponent);
|
|
111
|
+
|
|
112
|
+
expect(container.firstChild.classList).toContain('test-class');
|
|
52
113
|
});
|
|
53
114
|
|
|
54
115
|
it('has no automated accessibility violations', async () => {
|
|
55
|
-
const { container } =
|
|
56
|
-
slots: { default: 'Test Switch' },
|
|
57
|
-
});
|
|
116
|
+
const { container } = renderTestSwitch();
|
|
58
117
|
const results = await axe(container);
|
|
59
118
|
expect(results).toHaveNoViolations();
|
|
60
119
|
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { render } from '@testing-library/vue';
|
|
2
|
+
import { axe } from 'jest-axe';
|
|
3
|
+
import KvTabPanel from '../../../../vue/KvTabPanel.vue';
|
|
4
|
+
|
|
5
|
+
describe('KvTabPanel', () => {
|
|
6
|
+
it('has no automated accessibility violations', async () => {
|
|
7
|
+
const { container } = render(KvTabPanel);
|
|
8
|
+
const results = await axe(container);
|
|
9
|
+
expect(results).toHaveNoViolations();
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { render, fireEvent } from '@testing-library/vue';
|
|
2
|
+
import userEvent from '@testing-library/user-event';
|
|
3
|
+
import { axe } from 'jest-axe';
|
|
4
|
+
import '../../../../__mocks__/ResizeObserver';
|
|
5
|
+
import KvTab from '../../../../vue/KvTab.vue';
|
|
6
|
+
import KvTabPanel from '../../../../vue/KvTabPanel.vue';
|
|
7
|
+
import KvTabs from '../../../../vue/KvTabs.vue';
|
|
8
|
+
|
|
9
|
+
// jsdom does not include scrollIntoView, so define it to avoid errors when switching tabs
|
|
10
|
+
window.HTMLElement.prototype.scrollIntoView = () => {};
|
|
11
|
+
|
|
12
|
+
function renderOneTabSet() {
|
|
13
|
+
return render({
|
|
14
|
+
components: { KvTabs, KvTab, KvTabPanel },
|
|
15
|
+
template: `
|
|
16
|
+
<kv-tabs>
|
|
17
|
+
<template #tabNav>
|
|
18
|
+
<kv-tab forPanel="demo-1-first">First</kv-tab>
|
|
19
|
+
<kv-tab forPanel="demo-1-second">Second</kv-tab>
|
|
20
|
+
<kv-tab forPanel="demo-1-third">Third</kv-tab>
|
|
21
|
+
<kv-tab forPanel="demo-1-forth">Fourth is longer</kv-tab>
|
|
22
|
+
</template>
|
|
23
|
+
<template #tabPanels>
|
|
24
|
+
<kv-tab-panel id="demo-1-first"><p>First Panel</p></kv-tab-panel>
|
|
25
|
+
<kv-tab-panel id="demo-1-second"><p>Second Panel has <br>longer<br>content</p></kv-tab-panel>
|
|
26
|
+
<kv-tab-panel id="demo-1-third"><p>Third Panel</p></kv-tab-panel>
|
|
27
|
+
<kv-tab-panel id="demo-1-forth"><p>Fourth Panel</p></kv-tab-panel>
|
|
28
|
+
</template>
|
|
29
|
+
</kv-tabs>
|
|
30
|
+
`,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('KvTabs', () => {
|
|
35
|
+
it('has no automated accessibility violations', async () => {
|
|
36
|
+
const { container } = renderOneTabSet();
|
|
37
|
+
const results = await axe(container);
|
|
38
|
+
expect(results).toHaveNoViolations();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('renders with a role of "tablist", "tab", & "tabpanel"', async () => {
|
|
42
|
+
const { getByRole, getAllByRole } = renderOneTabSet();
|
|
43
|
+
expect(getByRole('tablist')).toBeDefined();
|
|
44
|
+
expect(getAllByRole('tab').length).toBe(4);
|
|
45
|
+
// expect(getByRole('tabpanel')).toBeDefined(); // TODO: make sure first panel reveals itself
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// TODO: it renders with the first slide active by default
|
|
49
|
+
|
|
50
|
+
it('switches between tab panels when tabs are clicked', async () => {
|
|
51
|
+
const { getByText } = renderOneTabSet();
|
|
52
|
+
|
|
53
|
+
// Click third tab and expect the third panel to be visible
|
|
54
|
+
await fireEvent.click(getByText('Third'));
|
|
55
|
+
expect(getByText('First Panel')).not.toBeVisible();
|
|
56
|
+
expect(getByText('Third Panel')).toBeVisible();
|
|
57
|
+
|
|
58
|
+
// Click first tab and expect the first panel to be visible
|
|
59
|
+
await fireEvent.click(getByText('First'));
|
|
60
|
+
expect(getByText('First Panel')).toBeVisible();
|
|
61
|
+
expect(getByText('Third Panel')).not.toBeVisible();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('switches between tab panels when the left and right arrows are pressed', async () => {
|
|
65
|
+
const { findByText } = renderOneTabSet();
|
|
66
|
+
|
|
67
|
+
// Focus on the first tab
|
|
68
|
+
const firstTab = await findByText('First');
|
|
69
|
+
firstTab.focus();
|
|
70
|
+
|
|
71
|
+
// Press arrow right twice and expect the third tab panel to be visible
|
|
72
|
+
userEvent.keyboard('{ArrowRight}{ArrowRight}');
|
|
73
|
+
expect(await findByText('Third Panel')).toBeVisible();
|
|
74
|
+
|
|
75
|
+
// Press arrow left once and expect the second tab panel to be visible
|
|
76
|
+
userEvent.keyboard('{ArrowLeft}');
|
|
77
|
+
expect(await findByText('Second Panel', { exact: false })).toBeVisible();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('goes to first panel when home key is pressed and last panel when end key is pressed', async () => {
|
|
81
|
+
const { findByText } = renderOneTabSet();
|
|
82
|
+
|
|
83
|
+
// Focus on the first tab
|
|
84
|
+
const firstTab = await findByText('First');
|
|
85
|
+
firstTab.focus();
|
|
86
|
+
|
|
87
|
+
// Press arrow right once and expect the second tab panel to be visible
|
|
88
|
+
userEvent.keyboard('{ArrowRight}');
|
|
89
|
+
expect(await findByText('Second Panel', { exact: false })).toBeVisible();
|
|
90
|
+
|
|
91
|
+
// Press end key and expect the fourth panel to be visible
|
|
92
|
+
userEvent.keyboard('{End}');
|
|
93
|
+
expect(await findByText('Fourth Panel')).toBeVisible();
|
|
94
|
+
|
|
95
|
+
// Press home key and expect the first panel to be visible
|
|
96
|
+
userEvent.keyboard('{Home}');
|
|
97
|
+
expect(await findByText('First Panel')).toBeVisible();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { render, fireEvent } from '@testing-library/vue';
|
|
2
2
|
import userEvent from '@testing-library/user-event';
|
|
3
|
-
import { axe
|
|
3
|
+
import { axe } from 'jest-axe';
|
|
4
4
|
import KvTextInput from '../../../../vue/KvTextInput.vue';
|
|
5
5
|
|
|
6
|
-
expect.extend(toHaveNoViolations);
|
|
7
|
-
|
|
8
6
|
const KvTextInputTemplate = {
|
|
9
7
|
components: { KvTextInput },
|
|
10
8
|
template: `
|
|
@@ -28,7 +26,7 @@ describe('KvTextInput', () => {
|
|
|
28
26
|
const textInputEl = getByRole('textbox');
|
|
29
27
|
|
|
30
28
|
expect(textInputEl.value).toEqual('');
|
|
31
|
-
|
|
29
|
+
userEvent.type(textInputEl, 'abc 123');
|
|
32
30
|
expect(textInputEl.value).toEqual('abc 123');
|
|
33
31
|
});
|
|
34
32
|
|
|
@@ -47,6 +45,81 @@ describe('KvTextInput', () => {
|
|
|
47
45
|
expect(textInputEl.value).toEqual('');
|
|
48
46
|
});
|
|
49
47
|
|
|
48
|
+
it('works with v-model', async () => {
|
|
49
|
+
const TestComponent = {
|
|
50
|
+
template:
|
|
51
|
+
`<div>
|
|
52
|
+
<label for="text-input">Text input</label>
|
|
53
|
+
<KvTextInput v-model="textValue" id="text-input" />
|
|
54
|
+
<button @click="textValue = 'abc'">reset</button>
|
|
55
|
+
<span>The text value is {{ textValue }}</span>
|
|
56
|
+
</div>`,
|
|
57
|
+
components: { KvTextInput },
|
|
58
|
+
data: () => ({ textValue: 'abc' }),
|
|
59
|
+
};
|
|
60
|
+
const { getByRole, getByText } = render(TestComponent);
|
|
61
|
+
const textInputEl = getByRole('textbox');
|
|
62
|
+
|
|
63
|
+
// Check that the value is 'abc' initially
|
|
64
|
+
expect(getByText('The text value is abc')).toBeDefined();
|
|
65
|
+
expect(textInputEl.value).toEqual('abc');
|
|
66
|
+
|
|
67
|
+
// Type 'def' in the text input and expect the value to be 'abcdef' now
|
|
68
|
+
await userEvent.type(textInputEl, 'def');
|
|
69
|
+
expect(getByText('The text value is abcdef')).toBeDefined();
|
|
70
|
+
expect(textInputEl.value).toEqual('abcdef');
|
|
71
|
+
|
|
72
|
+
// Click the reset button and expect the value to be 'abc' again
|
|
73
|
+
await fireEvent.click(getByText('reset'));
|
|
74
|
+
expect(getByText('The text value is abc')).toBeDefined();
|
|
75
|
+
expect(textInputEl.value).toEqual('abc');
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('applies parent event listeners to the input element', async () => {
|
|
79
|
+
const onInput = jest.fn();
|
|
80
|
+
const TestComponent = {
|
|
81
|
+
template: '<KvTextInput id="test" @input="onInput" />',
|
|
82
|
+
components: { KvTextInput },
|
|
83
|
+
methods: { onInput },
|
|
84
|
+
};
|
|
85
|
+
const { getByRole } = render(TestComponent);
|
|
86
|
+
|
|
87
|
+
const textInputEl = getByRole('textbox');
|
|
88
|
+
await userEvent.type(textInputEl, 'a');
|
|
89
|
+
expect(onInput.mock.calls.length).toBe(1);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('applies parent attributes to the input element', async () => {
|
|
93
|
+
const TestComponent = {
|
|
94
|
+
template: '<KvTextInput id="test" name="test-text-input" />',
|
|
95
|
+
components: { KvTextInput },
|
|
96
|
+
};
|
|
97
|
+
const { getByRole } = render(TestComponent);
|
|
98
|
+
|
|
99
|
+
const textInputEl = getByRole('textbox');
|
|
100
|
+
expect(textInputEl.name).toBe('test-text-input');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('applies parent styles to the root element', async () => {
|
|
104
|
+
const TestComponent = {
|
|
105
|
+
template: '<KvTextInput id="test" style="padding-top:1234px" />',
|
|
106
|
+
components: { KvTextInput },
|
|
107
|
+
};
|
|
108
|
+
const { container } = render(TestComponent);
|
|
109
|
+
|
|
110
|
+
expect(container.firstChild.style.paddingTop).toEqual('1234px');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('applies parent classes to the root element', async () => {
|
|
114
|
+
const TestComponent = {
|
|
115
|
+
template: '<KvTextInput id="test" class="test-class" />',
|
|
116
|
+
components: { KvTextInput },
|
|
117
|
+
};
|
|
118
|
+
const { container } = render(TestComponent);
|
|
119
|
+
|
|
120
|
+
expect(container.firstChild.classList).toContain('test-class');
|
|
121
|
+
});
|
|
122
|
+
|
|
50
123
|
it('has no automated accessibility violations', async () => {
|
|
51
124
|
const { container } = render(KvTextInputTemplate);
|
|
52
125
|
const results = await axe(container);
|
|
@@ -55,7 +128,7 @@ describe('KvTextInput', () => {
|
|
|
55
128
|
});
|
|
56
129
|
|
|
57
130
|
it('clear button cleans the input value', async () => {
|
|
58
|
-
const { getByRole } = render(KvTextInput, {
|
|
131
|
+
const { getByRole, findByRole, queryByRole } = render(KvTextInput, {
|
|
59
132
|
props: {
|
|
60
133
|
canClear: true,
|
|
61
134
|
valid: true,
|
|
@@ -63,12 +136,16 @@ describe('KvTextInput', () => {
|
|
|
63
136
|
},
|
|
64
137
|
});
|
|
65
138
|
const textInputEl = getByRole('textbox');
|
|
139
|
+
|
|
66
140
|
expect(textInputEl.value).toEqual('');
|
|
67
|
-
|
|
141
|
+
expect(queryByRole('button')).toBeNull();
|
|
142
|
+
|
|
143
|
+
userEvent.type(textInputEl, 'abc 123');
|
|
68
144
|
expect(textInputEl.value).toEqual('abc 123');
|
|
69
|
-
const
|
|
70
|
-
expect(
|
|
71
|
-
|
|
145
|
+
const buttonEl = await findByRole('button');
|
|
146
|
+
expect(buttonEl).toBeDefined();
|
|
147
|
+
|
|
148
|
+
await fireEvent.click(buttonEl);
|
|
72
149
|
expect(textInputEl.value).toEqual('');
|
|
73
150
|
});
|
|
74
151
|
});
|
|
@@ -1,41 +1,37 @@
|
|
|
1
1
|
import { render } from '@testing-library/vue';
|
|
2
|
-
import { axe
|
|
3
|
-
import
|
|
2
|
+
import { axe } from 'jest-axe';
|
|
3
|
+
import addVueRouter from '../../utils/addVueRouter';
|
|
4
4
|
import KvTextLink from '../../../../vue/KvTextLink.vue';
|
|
5
5
|
|
|
6
|
-
const router = new VueRouter();
|
|
7
|
-
|
|
8
|
-
expect.extend(toHaveNoViolations);
|
|
9
|
-
|
|
10
6
|
describe('Default Button', () => {
|
|
7
|
+
const renderTestTextLink = (options) => render(KvTextLink, addVueRouter({
|
|
8
|
+
slots: { default: 'Test Text Link' },
|
|
9
|
+
...options,
|
|
10
|
+
}));
|
|
11
|
+
|
|
11
12
|
it('renders as a button tag by default', () => {
|
|
12
|
-
const { getByRole } =
|
|
13
|
-
|
|
14
|
-
});
|
|
15
|
-
getByRole('button', { name: 'Test Button' });
|
|
13
|
+
const { getByRole } = renderTestTextLink();
|
|
14
|
+
getByRole('button', { name: 'Test Text Link' });
|
|
16
15
|
});
|
|
17
16
|
|
|
18
17
|
it('renders as an anchor tag when passed an href prop', () => {
|
|
19
|
-
const { getByRole } =
|
|
18
|
+
const { getByRole } = renderTestTextLink({
|
|
20
19
|
props: { href: 'https://www.example.com/' },
|
|
21
|
-
slots: { default: 'Test Button' },
|
|
22
20
|
});
|
|
23
|
-
const anchorEl = getByRole('link', { name: 'Test
|
|
21
|
+
const anchorEl = getByRole('link', { name: 'Test Text Link' });
|
|
24
22
|
expect(anchorEl.href).toEqual('https://www.example.com/');
|
|
25
23
|
});
|
|
26
24
|
|
|
27
25
|
it('renders as an anchor tag when passed a route string', () => {
|
|
28
|
-
const { getByRole } =
|
|
26
|
+
const { getByRole } = renderTestTextLink({
|
|
29
27
|
props: { to: '/home' },
|
|
30
|
-
slots: { default: 'Test Button' },
|
|
31
|
-
routes: router,
|
|
32
28
|
});
|
|
33
|
-
const anchorEl = getByRole('link', { name: 'Test
|
|
29
|
+
const anchorEl = getByRole('link', { name: 'Test Text Link' });
|
|
34
30
|
expect(anchorEl.href).toEqual('http://localhost/#/home');
|
|
35
31
|
});
|
|
36
32
|
|
|
37
33
|
it('renders as an anchor tag when passed a route object', () => {
|
|
38
|
-
const { getByRole } =
|
|
34
|
+
const { getByRole } = renderTestTextLink({
|
|
39
35
|
props: {
|
|
40
36
|
to: {
|
|
41
37
|
path: 'test-route-with-query',
|
|
@@ -44,17 +40,13 @@ describe('Default Button', () => {
|
|
|
44
40
|
},
|
|
45
41
|
},
|
|
46
42
|
},
|
|
47
|
-
slots: { default: 'Test Button' },
|
|
48
|
-
routes: router,
|
|
49
43
|
});
|
|
50
|
-
const anchorEl = getByRole('link', { name: 'Test
|
|
44
|
+
const anchorEl = getByRole('link', { name: 'Test Text Link' });
|
|
51
45
|
expect(anchorEl.href).toEqual('http://localhost/#/test-route-with-query?param1=a');
|
|
52
46
|
});
|
|
53
47
|
|
|
54
48
|
it('has no automated accessibility violations', async () => {
|
|
55
|
-
const { container } =
|
|
56
|
-
slots: { default: 'Test Button' },
|
|
57
|
-
});
|
|
49
|
+
const { container } = renderTestTextLink();
|
|
58
50
|
const results = await axe(container);
|
|
59
51
|
expect(results).toHaveNoViolations();
|
|
60
52
|
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { render } from '@testing-library/vue';
|
|
2
|
+
import { axe } from 'jest-axe';
|
|
3
|
+
import KvToast from '../../../../vue/KvToast.vue';
|
|
4
|
+
|
|
5
|
+
describe('KvToast', () => {
|
|
6
|
+
it('has no automated accessibility violations', async () => {
|
|
7
|
+
const { container } = render(KvToast);
|
|
8
|
+
const results = await axe(container);
|
|
9
|
+
expect(results).toHaveNoViolations();
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/* eslint-disable import/no-extraneous-dependencies */
|
|
2
|
+
import { isVue3 } from 'vue-demi';
|
|
3
|
+
import * as VueRouter from 'vue-router';
|
|
4
|
+
|
|
5
|
+
export default function addVueRouter(testingLibraryOptions, vueRouterOptions) {
|
|
6
|
+
const opts = { ...testingLibraryOptions };
|
|
7
|
+
|
|
8
|
+
if (isVue3) {
|
|
9
|
+
// create opts.global.plugins array if it does not exist
|
|
10
|
+
opts.global = opts.global ?? {};
|
|
11
|
+
opts.global.plugins = opts.global.plugins ?? [];
|
|
12
|
+
|
|
13
|
+
// add Vue Router to plugins array
|
|
14
|
+
opts.global.plugins.push(VueRouter.createRouter(vueRouterOptions ?? {
|
|
15
|
+
history: VueRouter.createWebHashHistory(),
|
|
16
|
+
routes: [{ path: '/:path(.*)', component: {} }],
|
|
17
|
+
}));
|
|
18
|
+
} else {
|
|
19
|
+
const VueRouterDefault = VueRouter.default;
|
|
20
|
+
opts.routes = new VueRouterDefault(vueRouterOptions);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return opts;
|
|
24
|
+
}
|