Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions showcase/tests/acceptance/components/hds/theming-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/

import { module, test } from 'qunit';
import { visit } from '@ember/test-helpers';
import { setupApplicationTest } from 'showcase/tests/helpers';
import { a11yAudit } from 'ember-a11y-testing/test-support';

module('Acceptance | Foundations | hds/theming', function (hooks) {
setupApplicationTest(hooks);

test('Foundations/hds/theming page passes automated a11y checks', async function (assert) {
await visit('/foundations/theming');
await a11yAudit();

assert.ok(true, 'a11y automation audit passed');
});
});
21 changes: 21 additions & 0 deletions showcase/tests/acceptance/percy-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,27 @@ module('Acceptance | Percy test', function (hooks) {
await visit('/foundations/focus-ring');
await percySnapshot('FocusRing');

await visit('/foundations/theming');
await percySnapshot('Theming - Standard');
await fillIn(
'.shw-theme-switcher__control-select',
'css-selectors--migration|default',
);
await percySnapshot('Theming - HDS Default');
await fillIn(
'.shw-theme-switcher__control-select',
'css-selectors--migration|light',
);
await percySnapshot('Theming - CDS Light');
await fillIn(
'.shw-theme-switcher__control-select',
'css-selectors--migration|dark',
);
await percySnapshot('Theming - CDS Dark');

// IMPORTANT: this is necessary, to reset the theme to its "standard"
await fillIn('.shw-theme-switcher__control-select', 'standard|');

// Take snapshots for English, Spanish, and None (Fallback) translations
await visit('/internationalization/translation');
await percySnapshot('Translation - English');
Expand Down
12 changes: 12 additions & 0 deletions showcase/tests/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@
<!-- 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 -->
<link rel="stylesheet" href="{{rootURL}}assets/styles/@hashicorp/design-system-components.css">

<!-- 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] -->
<link id="hds-tokens-with-css-selectors" rel="stylesheet" integrity="" disabled="true"
href="{{rootURL}}assets/styles/@hashicorp/themed-tokens/with-css-selectors/tokens.css">
<link id="hds-tokens-with-css-selectors--migration" rel="stylesheet" integrity="" disabled="true"
href="{{rootURL}}assets/styles/@hashicorp/themed-tokens/with-css-selectors--migration/tokens.css">
<link id="hds-tokens-with-css-selectors--advanced" rel="stylesheet" integrity="" disabled="true"
href="{{rootURL}}assets/styles/@hashicorp/themed-tokens/with-css-selectors--advanced/tokens.css">

<!-- this is the "components-only" CSS file for the HDS components, which needs to be associated with a themed CSS tokens file to work -->
<link id="hds-components-stylesheet-common" rel="stylesheet" integrity="" disabled="true"
href="{{rootURL}}assets/styles/@hashicorp/design-system-components-common.css">

<!-- these are the overriding styles for the Ember PowerSelect component -->
<link rel="stylesheet" href="{{rootURL}}assets/styles/@hashicorp/design-system-power-select-overrides.css">

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/

import { module, test } from 'qunit';
import { setupRenderingTest } from 'showcase/tests/helpers';
import { render, resetOnerror, setupOnerror } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';

import {
CONTEXTUAL_THEMES,
CONTEXTUAL_MODES,
} from '@hashicorp/design-system-components/components/hds/theme-context/index';

module('Integration | Component | hds/theme-context/index', function (hooks) {
setupRenderingTest(hooks);

hooks.afterEach(() => {
resetOnerror();
});

test('it should render the component with a CSS class that matches the component name', async function (assert) {
await render(
hbs`<Hds::ThemeContext @context="light" id="test-theme-context" />`,
);
assert.dom('#test-theme-context').hasClass('hds-theme-context');
});

// CONTEXT - THEMES

CONTEXTUAL_THEMES.forEach((context) => {
test(`it should render the correct CSS "theme" class for "${context}" context`, async function (assert) {
this.set('context', context);
await render(
hbs`<Hds::ThemeContext @context={{this.context}} id="test-theme-context" />`,
);
assert.dom('#test-theme-context').hasClass(`hds-theme-${context}`);
});
});

// CONTEXT - MODES

CONTEXTUAL_MODES.forEach((context) => {
test(`it should render the correct CSS "mode" class for "${context}" context`, async function (assert) {
this.set('context', context);
await render(
hbs`<Hds::ThemeContext @context={{this.context}} id="test-theme-context" />`,
);
assert.dom('#test-theme-context').hasClass(`hds-mode-${context}`);
});
});

// YIELDED CONTENT

test('it correctly yields content passed in the default block', async function (assert) {
await render(hbs`
<Hds::ThemeContext @context="light" id="test-theme-context">
<p id="test-content">This is yielded content</p>
</Hds::ThemeContext>
`);
assert.dom('#test-content').exists();
Copy link
Contributor

Choose a reason for hiding this comment

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

This one seems a bit redundant perhaps.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We test to test the yielding of content. This one in particular makes sense to me, since it's one of the two main functions of this component (apply a class and yield the content)

assert.dom('#test-content').hasText('This is yielded content');
assert.dom('#test-theme-context #test-content').exists();
});

// ASSERTIONS

test('it should throw an assertion if an incorrect value for @context is provided', async function (assert) {
const errorMessage =
'@context for "Hds::ThemeContext" must be one of the following: default, system, light, dark, cds-g0, cds-g10, cds-g90, cds-g100; received: foo';
assert.expect(2);
setupOnerror(function (error) {
assert.strictEqual(error.message, `Assertion Failed: ${errorMessage}`);
});
await render(hbs`<Hds::ThemeContext @context="foo" />`);
assert.throws(function () {
throw new Error(errorMessage);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: MPL-2.0
*/

import { module, test } from 'qunit';
import { setupRenderingTest } from 'showcase/tests/helpers';
import { render, click } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import sinon from 'sinon';

module('Integration | Component | hds/theme-switcher/index', function (hooks) {
setupRenderingTest(hooks);

hooks.beforeEach(function () {
this.themingService = this.owner.lookup('service:hds-theming');
});

hooks.afterEach(function () {
// Reset the theme after each test
this.themingService.setTheme({ theme: undefined });
});

test('it should render the component with a CSS class that matches the component name', async function (assert) {
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
assert.dom('#test-theme-switcher').hasClass('hds-theme-switcher-control');
});

// TOGGLE SIZE

test('it should render with small size by default', async function (assert) {
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
assert
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
.hasClass('hds-dropdown-toggle-button--size-small');
});

test('it should render the correct CSS size class if @toggleSize is declared', async function (assert) {
await render(
hbs`<Hds::ThemeSwitcher @toggleSize="medium" id="test-theme-switcher" />`,
);
assert
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
.hasClass('hds-dropdown-toggle-button--size-medium');
});

// TOGGLE IS FULL WIDTH

test('it should not be full width by default', async function (assert) {
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
assert
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
.doesNotHaveClass('hds-button--width-full');
});

test('it should render full width if @toggleIsFullWidth is true', async function (assert) {
await render(
hbs`<Hds::ThemeSwitcher @toggleIsFullWidth={{true}} id="test-theme-switcher" />`,
);
assert
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
.hasClass('hds-dropdown-toggle-button--width-full');
});

// THEME DISPLAY

test('it should display "Theme" label when no theme is set', async function (assert) {
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
assert
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
.containsText('Theme');
});

test('it should display "System" label when system theme is set', async function (assert) {
this.themingService.setTheme({ theme: 'system' });
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
assert
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
.containsText('System');
});

test('it should display "Light" label when light theme is set', async function (assert) {
this.themingService.setTheme({ theme: 'light' });
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
assert
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
.containsText('Light');
});

test('it should display "Dark" label when dark theme is set', async function (assert) {
this.themingService.setTheme({ theme: 'dark' });
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
assert
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
.containsText('Dark');
});

test('it should display "Default" label when default theme is set and hasDefaultOption is true', async function (assert) {
this.themingService.setTheme({ theme: 'default' });
await render(
hbs`<Hds::ThemeSwitcher @hasDefaultOption={{true}} id="test-theme-switcher" />`,
);
assert
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
.containsText('Default');
});

// HAS DEFAULT OPTION

test('it should not include the "Default" option by default', async function (assert) {
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
await click('#test-theme-switcher button');
assert.dom('.hds-dropdown-list-item').exists({ count: 3 });
assert.dom('.hds-dropdown-list-item').doesNotContainText('Default');
});

test('it should include the "Default" option when `@hasDefaultOption` is `true`', async function (assert) {
await render(
hbs`<Hds::ThemeSwitcher @hasDefaultOption={{true}} id="test-theme-switcher" />`,
);
await click('#test-theme-switcher button');
assert.dom('.hds-dropdown-list-item').exists({ count: 4 });
assert.dom('.hds-dropdown-list-item').containsText('Default');
});

// HAS SYSTEM OPTION

test('it should include the "System" option by default', async function (assert) {
await render(hbs`<Hds::ThemeSwitcher id="test-theme-switcher" />`);
await click('#test-theme-switcher button');
assert.dom('.hds-dropdown-list-item').containsText('System');
});

test('it should not include the "System" option when `@hasSystemOption` is `false`', async function (assert) {
await render(
hbs`<Hds::ThemeSwitcher @hasSystemOption={{false}} id="test-theme-switcher" />`,
);
await click('#test-theme-switcher button');
assert.dom('.hds-dropdown-list-item').exists({ count: 2 });
assert.dom('.hds-dropdown-list-item').doesNotContainText('System');
});

// THEME SELECTION

test('it should update the theme in the service and the label in the toggle when a dropdown option is selected', async function (assert) {
await render(
hbs`<Hds::ThemeSwitcher @hasDefaultOption={{true}} @hasSystemOption={{true}} id="test-theme-switcher" />`,
);

// open the dropdown
await click('#test-theme-switcher button');

// click on `Light` theme
await click('.hds-dropdown-list-item:nth-of-type(3) button');
assert.strictEqual(
this.themingService.currentTheme,
'light',
'theme service should be updated to `light`',
);
assert
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
.containsText('Light');

// click on `Dark` theme
await click('.hds-dropdown-list-item:nth-of-type(4) button');
assert.strictEqual(
this.themingService.currentTheme,
'dark',
'theme service should be updated to `dark`',
);
assert
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
.containsText('Dark');

// click on `System` theme
await click('.hds-dropdown-list-item:nth-of-type(2) button');
assert.strictEqual(
this.themingService.currentTheme,
'system',
'theme service should be updated to `system`',
);
assert
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
.containsText('System');

// click on `Default` theme
await click('.hds-dropdown-list-item:nth-of-type(1) button');
assert.strictEqual(
this.themingService.currentTheme,
'default',
'theme service should be updated to `undefined` (default)',
);
assert
.dom('#test-theme-switcher .hds-dropdown-toggle-button')
.containsText('Default');
});

// CALLBACKS

test('it should call @onSetTheme callback when provided', async function (assert) {
const onSetTheme = sinon.spy();

this.set('onSetTheme', onSetTheme);

await render(
hbs`<Hds::ThemeSwitcher @onSetTheme={{this.onSetTheme}} id="test-theme-switcher" />`,
);
await click('#test-theme-switcher button');

// change theme
const lightOptionButton = this.element.querySelector(
'.hds-dropdown-list-item:nth-of-type(3) button',
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there another way the button could be selected that doesn't rely solely on the DOM structure like this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not that I could think of (we could use the inner text, but the logic would become even more complex)

);
await click(lightOptionButton);

assert.true(onSetTheme.calledOnce);
});
});
Loading