Skip to content

Commit fd69b4d

Browse files
didoodchyunKristinLBradley
committed
[Project Solar / Phase 1 / Engineering Follow-ups] Add tests for HDS theming service and components (#3428)
Co-authored-by: Dylan Hyun <dylan.hyun@hashicorp.com> Co-authored-by: Kristin Bradley <kristin.bradley@hashicorp.com>
1 parent 815ba83 commit fd69b4d

File tree

6 files changed

+737
-0
lines changed

6 files changed

+737
-0
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Copyright (c) HashiCorp, Inc.
3+
* SPDX-License-Identifier: MPL-2.0
4+
*/
5+
6+
import { module, test } from 'qunit';
7+
import { visit } from '@ember/test-helpers';
8+
import { setupApplicationTest } from 'showcase/tests/helpers';
9+
import { a11yAudit } from 'ember-a11y-testing/test-support';
10+
11+
module('Acceptance | Foundations | hds/theming', function (hooks) {
12+
setupApplicationTest(hooks);
13+
14+
test('Foundations/hds/theming page passes automated a11y checks', async function (assert) {
15+
await visit('/foundations/theming');
16+
await a11yAudit();
17+
18+
assert.ok(true, 'a11y automation audit passed');
19+
});
20+
});

showcase/tests/acceptance/percy-test.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,27 @@ module('Acceptance | Percy test', function (hooks) {
3131
await visit('/foundations/focus-ring');
3232
await percySnapshot('FocusRing');
3333

34+
await visit('/foundations/theming');
35+
await percySnapshot('Theming - Standard');
36+
await fillIn(
37+
'.shw-theme-switcher__control-select',
38+
'css-selectors--migration|default',
39+
);
40+
await percySnapshot('Theming - HDS Default');
41+
await fillIn(
42+
'.shw-theme-switcher__control-select',
43+
'css-selectors--migration|light',
44+
);
45+
await percySnapshot('Theming - CDS Light');
46+
await fillIn(
47+
'.shw-theme-switcher__control-select',
48+
'css-selectors--migration|dark',
49+
);
50+
await percySnapshot('Theming - CDS Dark');
51+
52+
// IMPORTANT: this is necessary, to reset the theme to its "standard"
53+
await fillIn('.shw-theme-switcher__control-select', 'standard|');
54+
3455
// Take snapshots for English, Spanish, and None (Fallback) translations
3556
await visit('/internationalization/translation');
3657
await percySnapshot('Translation - English');

showcase/tests/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,18 @@
2121
<!-- this is the "standard" (non-themed) CSS file for the HDS components, which contains both the default HDS tokens as well as the HDS components' styles -->
2222
<link rel="stylesheet" href="{{rootURL}}assets/styles/@hashicorp/design-system-components.css">
2323

24+
<!-- these are the design tokens (CSS variables) for theming [NOTE: they are disabled by default and toggled on/off via JS by the `ShwThemeSwitcher` selector] -->
25+
<link id="hds-tokens-with-css-selectors" rel="stylesheet" integrity="" disabled="true"
26+
href="{{rootURL}}assets/styles/@hashicorp/themed-tokens/with-css-selectors/tokens.css">
27+
<link id="hds-tokens-with-css-selectors--migration" rel="stylesheet" integrity="" disabled="true"
28+
href="{{rootURL}}assets/styles/@hashicorp/themed-tokens/with-css-selectors--migration/tokens.css">
29+
<link id="hds-tokens-with-css-selectors--advanced" rel="stylesheet" integrity="" disabled="true"
30+
href="{{rootURL}}assets/styles/@hashicorp/themed-tokens/with-css-selectors--advanced/tokens.css">
31+
32+
<!-- this is the "components-only" CSS file for the HDS components, which needs to be associated with a themed CSS tokens file to work -->
33+
<link id="hds-components-stylesheet-common" rel="stylesheet" integrity="" disabled="true"
34+
href="{{rootURL}}assets/styles/@hashicorp/design-system-components-common.css">
35+
2436
<!-- these are the overriding styles for the Ember PowerSelect component -->
2537
<link rel="stylesheet" href="{{rootURL}}assets/styles/@hashicorp/design-system-power-select-overrides.css">
2638

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/**
2+
* Copyright (c) HashiCorp, Inc.
3+
* SPDX-License-Identifier: MPL-2.0
4+
*/
5+
6+
import { module, test } from 'qunit';
7+
import { setupRenderingTest } from 'showcase/tests/helpers';
8+
import { render, resetOnerror, setupOnerror } from '@ember/test-helpers';
9+
import { hbs } from 'ember-cli-htmlbars';
10+
11+
import {
12+
CONTEXTUAL_THEMES,
13+
CONTEXTUAL_MODES,
14+
} from '@hashicorp/design-system-components/components/hds/theme-context/index';
15+
16+
module('Integration | Component | hds/theme-context/index', function (hooks) {
17+
setupRenderingTest(hooks);
18+
19+
hooks.afterEach(() => {
20+
resetOnerror();
21+
});
22+
23+
test('it should render the component with a CSS class that matches the component name', async function (assert) {
24+
await render(
25+
hbs`<Hds::ThemeContext @context="light" id="test-theme-context" />`,
26+
);
27+
assert.dom('#test-theme-context').hasClass('hds-theme-context');
28+
});
29+
30+
// CONTEXT - THEMES
31+
32+
CONTEXTUAL_THEMES.forEach((context) => {
33+
test(`it should render the correct CSS "theme" class for "${context}" context`, async function (assert) {
34+
this.set('context', context);
35+
await render(
36+
hbs`<Hds::ThemeContext @context={{this.context}} id="test-theme-context" />`,
37+
);
38+
assert.dom('#test-theme-context').hasClass(`hds-theme-${context}`);
39+
});
40+
});
41+
42+
// CONTEXT - MODES
43+
44+
CONTEXTUAL_MODES.forEach((context) => {
45+
test(`it should render the correct CSS "mode" class for "${context}" context`, async function (assert) {
46+
this.set('context', context);
47+
await render(
48+
hbs`<Hds::ThemeContext @context={{this.context}} id="test-theme-context" />`,
49+
);
50+
assert.dom('#test-theme-context').hasClass(`hds-mode-${context}`);
51+
});
52+
});
53+
54+
// YIELDED CONTENT
55+
56+
test('it correctly yields content passed in the default block', async function (assert) {
57+
await render(hbs`
58+
<Hds::ThemeContext @context="light" id="test-theme-context">
59+
<p id="test-content">This is yielded content</p>
60+
</Hds::ThemeContext>
61+
`);
62+
assert.dom('#test-content').exists();
63+
assert.dom('#test-content').hasText('This is yielded content');
64+
assert.dom('#test-theme-context #test-content').exists();
65+
});
66+
67+
// ASSERTIONS
68+
69+
test('it should throw an assertion if an incorrect value for @context is provided', async function (assert) {
70+
const errorMessage =
71+
'@context for "Hds::ThemeContext" must be one of the following: default, system, light, dark, cds-g0, cds-g10, cds-g90, cds-g100; received: foo';
72+
assert.expect(2);
73+
setupOnerror(function (error) {
74+
assert.strictEqual(error.message, `Assertion Failed: ${errorMessage}`);
75+
});
76+
await render(hbs`<Hds::ThemeContext @context="foo" />`);
77+
assert.throws(function () {
78+
throw new Error(errorMessage);
79+
});
80+
});
81+
});
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
/**
2+
* Copyright (c) HashiCorp, Inc.
3+
* SPDX-License-Identifier: MPL-2.0
4+
*/
5+
6+
import { module, test } from 'qunit';
7+
import { setupRenderingTest } from 'showcase/tests/helpers';
8+
import { render, click } from '@ember/test-helpers';
9+
import { hbs } from 'ember-cli-htmlbars';
10+
import sinon from 'sinon';
11+
12+
module('Integration | Component | hds/theme-switcher/index', function (hooks) {
13+
setupRenderingTest(hooks);
14+
15+
hooks.beforeEach(function () {
16+
this.themingService = this.owner.lookup('service:hds-theming');
17+
});
18+
19+
hooks.afterEach(function () {
20+
// Reset the theme after each test
21+
this.themingService.setTheme({ theme: undefined });
22+
});
23+
24+
test('it should render the component with a CSS class that matches the component name', async function (assert) {
25+
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
26+
assert.dom('#test-theme-switcher').hasClass('hds-theme-switcher-control');
27+
});
28+
29+
// TOGGLE SIZE
30+
31+
test('it should render with small size by default', async function (assert) {
32+
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
33+
assert
34+
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
35+
.hasClass('hds-dropdown-toggle-button--size-small');
36+
});
37+
38+
test('it should render the correct CSS size class if @toggleSize is declared', async function (assert) {
39+
await render(
40+
hbs`<Hds::ThemeSwitcher @toggleSize="medium" id="test-theme-switcher" />`,
41+
);
42+
assert
43+
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
44+
.hasClass('hds-dropdown-toggle-button--size-medium');
45+
});
46+
47+
// TOGGLE IS FULL WIDTH
48+
49+
test('it should not be full width by default', async function (assert) {
50+
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
51+
assert
52+
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
53+
.doesNotHaveClass('hds-button--width-full');
54+
});
55+
56+
test('it should render full width if @toggleIsFullWidth is true', async function (assert) {
57+
await render(
58+
hbs`<Hds::ThemeSwitcher @toggleIsFullWidth={{true}} id="test-theme-switcher" />`,
59+
);
60+
assert
61+
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
62+
.hasClass('hds-dropdown-toggle-button--width-full');
63+
});
64+
65+
// THEME DISPLAY
66+
67+
test('it should display "Theme" label when no theme is set', async function (assert) {
68+
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
69+
assert
70+
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
71+
.containsText('Theme');
72+
});
73+
74+
test('it should display "System" label when system theme is set', async function (assert) {
75+
this.themingService.setTheme({ theme: 'system' });
76+
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
77+
assert
78+
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
79+
.containsText('System');
80+
});
81+
82+
test('it should display "Light" label when light theme is set', async function (assert) {
83+
this.themingService.setTheme({ theme: 'light' });
84+
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
85+
assert
86+
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
87+
.containsText('Light');
88+
});
89+
90+
test('it should display "Dark" label when dark theme is set', async function (assert) {
91+
this.themingService.setTheme({ theme: 'dark' });
92+
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
93+
assert
94+
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
95+
.containsText('Dark');
96+
});
97+
98+
test('it should display "Default" label when default theme is set and hasDefaultOption is true', async function (assert) {
99+
this.themingService.setTheme({ theme: 'default' });
100+
await render(
101+
hbs`<Hds::ThemeSwitcher @hasDefaultOption={{true}} id="test-theme-switcher" />`,
102+
);
103+
assert
104+
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
105+
.containsText('Default');
106+
});
107+
108+
// HAS DEFAULT OPTION
109+
110+
test('it should not include the "Default" option by default', async function (assert) {
111+
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
112+
await click('#test-theme-switcher button');
113+
assert.dom('.hds-dropdown-list-item').exists({ count: 3 });
114+
assert.dom('.hds-dropdown-list-item').doesNotContainText('Default');
115+
});
116+
117+
test('it should include the "Default" option when `@hasDefaultOption` is `true`', async function (assert) {
118+
await render(
119+
hbs`<Hds::ThemeSwitcher @hasDefaultOption={{true}} id="test-theme-switcher" />`,
120+
);
121+
await click('#test-theme-switcher button');
122+
assert.dom('.hds-dropdown-list-item').exists({ count: 4 });
123+
assert.dom('.hds-dropdown-list-item').containsText('Default');
124+
});
125+
126+
// HAS SYSTEM OPTION
127+
128+
test('it should include the "System" option by default', async function (assert) {
129+
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
130+
await click('#test-theme-switcher button');
131+
assert.dom('.hds-dropdown-list-item').containsText('System');
132+
});
133+
134+
test('it should not include the "System" option when `@hasSystemOption` is `false`', async function (assert) {
135+
await render(
136+
hbs`<Hds::ThemeSwitcher @hasSystemOption={{false}} id="test-theme-switcher" />`,
137+
);
138+
await click('#test-theme-switcher button');
139+
assert.dom('.hds-dropdown-list-item').exists({ count: 2 });
140+
assert.dom('.hds-dropdown-list-item').doesNotContainText('System');
141+
});
142+
143+
// THEME SELECTION
144+
145+
test('it should update the theme in the service and the label in the toggle when a dropdown option is selected', async function (assert) {
146+
await render(
147+
hbs`<Hds::ThemeSwitcher @hasDefaultOption={{true}} @hasSystemOption={{true}} id="test-theme-switcher" />`,
148+
);
149+
150+
// open the dropdown
151+
await click('#test-theme-switcher button');
152+
153+
// click on `Light` theme
154+
await click('.hds-dropdown-list-item:nth-of-type(3) button');
155+
assert.strictEqual(
156+
this.themingService.currentTheme,
157+
'light',
158+
'theme service should be updated to `light`',
159+
);
160+
assert
161+
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
162+
.containsText('Light');
163+
164+
// click on `Dark` theme
165+
await click('.hds-dropdown-list-item:nth-of-type(4) button');
166+
assert.strictEqual(
167+
this.themingService.currentTheme,
168+
'dark',
169+
'theme service should be updated to `dark`',
170+
);
171+
assert
172+
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
173+
.containsText('Dark');
174+
175+
// click on `System` theme
176+
await click('.hds-dropdown-list-item:nth-of-type(2) button');
177+
assert.strictEqual(
178+
this.themingService.currentTheme,
179+
'system',
180+
'theme service should be updated to `system`',
181+
);
182+
assert
183+
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
184+
.containsText('System');
185+
186+
// click on `Default` theme
187+
await click('.hds-dropdown-list-item:nth-of-type(1) button');
188+
assert.strictEqual(
189+
this.themingService.currentTheme,
190+
'default',
191+
'theme service should be updated to `undefined` (default)',
192+
);
193+
assert
194+
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
195+
.containsText('Default');
196+
});
197+
198+
// CALLBACKS
199+
200+
test('it should call @onSetTheme callback when provided', async function (assert) {
201+
const onSetTheme = sinon.spy();
202+
203+
this.set('onSetTheme', onSetTheme);
204+
205+
await render(
206+
hbs`<Hds::ThemeSwitcher @onSetTheme={{this.onSetTheme}} id="test-theme-switcher" />`,
207+
);
208+
await click('#test-theme-switcher button');
209+
210+
// change theme
211+
const lightOptionButton = this.element.querySelector(
212+
'.hds-dropdown-list-item:nth-of-type(3) button',
213+
);
214+
await click(lightOptionButton);
215+
216+
assert.true(onSetTheme.calledOnce);
217+
});
218+
});

0 commit comments

Comments
 (0)