From d524a9674f84529aad616c76ef21c3ea6a2de78c Mon Sep 17 00:00:00 2001 From: Julien Vannier Date: Fri, 27 Feb 2026 18:11:19 +0100 Subject: [PATCH 1/2] Add new assertion for infinite-select-option --- .../infinite-select-option.stories.mdx | 134 ++++++ .../infinite-select-option.ts | 401 ++++++++++++++++++ addon-test-support/register-assertions.ts | 5 +- .../infinite-select-option-test.ts | 288 +++++++++++++ 4 files changed, 826 insertions(+), 2 deletions(-) create mode 100644 addon-test-support/custom-assertions/infinite-select-option.stories.mdx create mode 100644 addon-test-support/custom-assertions/infinite-select-option.ts create mode 100644 tests/unit/custom-assertions/infinite-select-option-test.ts diff --git a/addon-test-support/custom-assertions/infinite-select-option.stories.mdx b/addon-test-support/custom-assertions/infinite-select-option.stories.mdx new file mode 100644 index 000000000..72d807639 --- /dev/null +++ b/addon-test-support/custom-assertions/infinite-select-option.stories.mdx @@ -0,0 +1,134 @@ +import { Meta } from '@storybook/addon-docs'; + + + +# InfiniteSelectOption Assertion + +The `infiniteSelectOption` custom assertion provides a fluent API for testing `OSS::InfiniteSelect::Option` components in your QUnit tests. + +## Usage + +```typescript +assert.infiniteSelectOption('').hasTitle('Option Title'); +``` + +## Installation + +This assertion is automatically registered when you call `registerAssertions(assert)` in your test setup. + +## API Reference + +### Element Existence +- `.exists(message?: string)` - Assert option exists +- `.doesNotExist(message?: string)` - Assert option doesn't exist + +### Content +- `.hasTitle(title: string, message?: string)` - Assert title text +- `.hasSubtitle(subtitle: string, message?: string)` - Assert subtitle text +- `.doesNotHaveSubtitle(message?: string)` - Assert no subtitle + +### State +- `.isSelected(message?: string)` - Assert selected state +- `.isNotSelected(message?: string)` - Assert not selected +- `.isDisabled(message?: string)` - Assert disabled state +- `.isNotDisabled(message?: string)` - Assert not disabled +- `.hasSelectionType(type: 'single' | 'multiple', message?: string)` - Assert selection type + +### Prefix Elements +- `.hasPrefixAvatar(message?: string)` - Assert has prefix avatar +- `.doesNotHavePrefixAvatar(message?: string)` - Assert no prefix avatar +- `.hasPrefixBadge(message?: string)` - Assert has prefix badge +- `.doesNotHavePrefixBadge(message?: string)` - Assert no prefix badge +- `.hasPrefixIcon(message?: string)` - Assert has prefix icon +- `.doesNotHavePrefixIcon(message?: string)` - Assert no prefix icon +- `.hasPrefixCountry(countryCode: string, message?: string)` - Assert has country flag +- `.doesNotHavePrefixCountry(message?: string)` - Assert no country flag + +### Main Icon +- `.hasIcon(message?: string)` - Assert has main icon +- `.doesNotHaveIcon(message?: string)` - Assert no main icon + +### Suffix Elements +- `.hasSuffixHint(hint: string, message?: string)` - Assert suffix hint text +- `.doesNotHaveSuffixHint(message?: string)` - Assert no suffix hint +- `.hasSuffixTag(message?: string)` - Assert has suffix tag +- `.doesNotHaveSuffixTag(message?: string)` - Assert no suffix tag +- `.hasSuffixIcon(message?: string)` - Assert has suffix icon +- `.doesNotHaveSuffixIcon(message?: string)` - Assert no suffix icon + +## Complete Example + +```typescript +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; +import sinon from 'sinon'; + +module('Integration | Component | my-component', function (hooks) { + setupRenderingTest(hooks); + + test('it renders the option with all features', async function (assert) { + this.onSelect = sinon.stub(); + this.prefixAvatar = { initials: 'JD' }; + this.suffixTag = { label: 'Premium', skin: 'primary' }; + + await render(hbs\` + + \`); + + // All these assertions will pass + assert.infiniteSelectOption('.oss-infinite-select-option').exists(); + assert.infiniteSelectOption('.oss-infinite-select-option').hasTitle('John Doe'); + assert.infiniteSelectOption('.oss-infinite-select-option').hasSubtitle('Premium User'); + assert.infiniteSelectOption('.oss-infinite-select-option').hasPrefixAvatar(); + assert.infiniteSelectOption('.oss-infinite-select-option').hasSuffixTag(); + assert.infiniteSelectOption('.oss-infinite-select-option').hasSuffixHint('+10'); + assert.infiniteSelectOption('.oss-infinite-select-option').isSelected(); + assert.infiniteSelectOption('.oss-infinite-select-option').hasSelectionType('single'); + assert.infiniteSelectOption('.oss-infinite-select-option').isNotDisabled(); + }); +}); +``` + +## Migrating Existing Tests + +### Before (using assert.dom) +```typescript +test('it renders the option', async function (assert) { + await render(hbs``); + + assert.dom('.oss-infinite-select-option').exists(); + assert.dom('.oss-infinite-select-option__title').hasText('Test'); + assert.dom('.oss-infinite-select-option--selected').exists(); + assert.dom('.oss-infinite-select-option--disabled').doesNotExist(); +}); +``` + +### After (using custom assertion) +```typescript +test('it renders the option', async function (assert) { + await render(hbs``); + + assert.infiniteSelectOption('.oss-infinite-select-option').exists(); + assert.infiniteSelectOption('.oss-infinite-select-option').hasTitle('Test'); + assert.infiniteSelectOption('.oss-infinite-select-option').isSelected(); + assert.infiniteSelectOption('.oss-infinite-select-option').isNotDisabled(); +}); +``` + diff --git a/addon-test-support/custom-assertions/infinite-select-option.ts b/addon-test-support/custom-assertions/infinite-select-option.ts new file mode 100644 index 000000000..48339d108 --- /dev/null +++ b/addon-test-support/custom-assertions/infinite-select-option.ts @@ -0,0 +1,401 @@ +import * as QUnit from 'qunit'; +import { isEmpty } from '@ember/utils'; + +export type SelectionType = 'single' | 'multiple' | undefined; + +export interface InfiniteSelectOptionAssertions { + exists(_message?: string): void; + doesNotExist(_message?: string): void; + hasTitle(_title: string, _message?: string): void; + hasSubtitle(_subtitle: string, _message?: string): void; + doesNotHaveSubtitle(_message?: string): void; + isSelected(_message?: string): void; + isNotSelected(_message?: string): void; + isDisabled(_message?: string): void; + isNotDisabled(_message?: string): void; + hasSelectionType(_selectionType: SelectionType, _message?: string): void; + hasPrefixAvatar(_message?: string): void; + doesNotHavePrefixAvatar(_message?: string): void; + hasPrefixBadge(_message?: string): void; + doesNotHavePrefixBadge(_message?: string): void; + hasPrefixIcon(_message?: string): void; + doesNotHavePrefixIcon(_message?: string): void; + hasPrefixCountry(_countryCode: string, _message?: string): void; + doesNotHavePrefixCountry(_message?: string): void; + hasIcon(_message?: string): void; + doesNotHaveIcon(_message?: string): void; + hasSuffixHint(_hint: string, _message?: string): void; + doesNotHaveSuffixHint(_message?: string): void; + hasSuffixTag(_message?: string): void; + doesNotHaveSuffixTag(_message?: string): void; + hasSuffixIcon(_message?: string): void; + doesNotHaveSuffixIcon(_message?: string): void; +} + +const assertion = (selector: string) => { + const getElement = (): Element | null => { + return document.querySelector(selector); + }; + + const assertElementExists = (methodName: string): Element => { + const element = getElement(); + if (!element) { + throw new Error( + `[assert.infinite-select-option] Element with selector "${selector}" not found. Make sure the component is rendered before calling .${methodName}()` + ); + } + return element; + }; + + return { + exists: (message?: string) => { + const element = getElement(); + const result = element !== null && element.classList.contains('oss-infinite-select-option'); + + QUnit.assert.pushResult({ + result, + actual: element !== null, + expected: true, + message: message ?? `InfiniteSelect::Option exists at selector "${selector}"` + }); + }, + + doesNotExist: (message?: string) => { + const element = getElement(); + const result = element === null || !element.classList.contains('oss-infinite-select-option'); + + QUnit.assert.pushResult({ + result, + actual: element === null, + expected: true, + message: message ?? `InfiniteSelect::Option does not exist at selector "${selector}"` + }); + }, + + hasTitle: (title: string, message?: string) => { + const element = assertElementExists('hasTitle'); + const titleElement = element.querySelector('.oss-infinite-select-option__title'); + const actual = titleElement ? (titleElement as HTMLElement).textContent?.trim() : ''; + const result = actual === title; + + QUnit.assert.pushResult({ + result, + actual, + expected: title, + message: message ?? `InfiniteSelect::Option has title "${title}"` + }); + }, + + hasSubtitle: (subtitle: string, message?: string) => { + const element = assertElementExists('hasSubtitle'); + const subtitleElement = element.querySelector('.oss-infinite-select-option__subtitle'); + const actual = subtitleElement ? (subtitleElement as HTMLElement).textContent?.trim() : ''; + const result = actual === subtitle; + + QUnit.assert.pushResult({ + result, + actual, + expected: subtitle, + message: message ?? `InfiniteSelect::Option has subtitle "${subtitle}"` + }); + }, + + doesNotHaveSubtitle: (message?: string) => { + const element = assertElementExists('doesNotHaveSubtitle'); + const subtitleElement = element.querySelector('.oss-infinite-select-option__subtitle'); + const actual = subtitleElement ? (subtitleElement as HTMLElement).textContent?.trim() : undefined; + const result = isEmpty(actual); + + QUnit.assert.pushResult({ + result, + actual, + expected: undefined, + message: message ?? 'InfiniteSelect::Option does not have a subtitle' + }); + }, + + isSelected: (message?: string) => { + const element = assertElementExists('isSelected'); + const result = element.classList.contains('oss-infinite-select-option--selected'); + + QUnit.assert.pushResult({ + result, + actual: result, + expected: true, + message: message ?? 'InfiniteSelect::Option is selected' + }); + }, + + isNotSelected: (message?: string) => { + const element = assertElementExists('isNotSelected'); + const result = !element.classList.contains('oss-infinite-select-option--selected'); + + QUnit.assert.pushResult({ + result, + actual: !result, + expected: false, + message: message ?? 'InfiniteSelect::Option is not selected' + }); + }, + + isDisabled: (message?: string) => { + const element = assertElementExists('isDisabled'); + const result = element.classList.contains('oss-infinite-select-option--disabled'); + + QUnit.assert.pushResult({ + result, + actual: result, + expected: true, + message: message ?? 'InfiniteSelect::Option is disabled' + }); + }, + + isNotDisabled: (message?: string) => { + const element = assertElementExists('isNotDisabled'); + const result = !element.classList.contains('oss-infinite-select-option--disabled'); + + QUnit.assert.pushResult({ + result, + actual: !result, + expected: false, + message: message ?? 'InfiniteSelect::Option is not disabled' + }); + }, + + hasSelectionType: (selectionType: SelectionType, message?: string) => { + const element = assertElementExists('hasSelectionType'); + const type = selectionType ?? 'single'; + const result = element.classList.contains(`oss-infinite-select-option--${type}`); + + QUnit.assert.pushResult({ + result, + actual: element.classList.contains('oss-infinite-select-option--single') + ? 'single' + : element.classList.contains('oss-infinite-select-option--multiple') + ? 'multiple' + : undefined, + expected: type, + message: message ?? `InfiniteSelect::Option has selectionType "${type}"` + }); + }, + + hasPrefixAvatar: (message?: string) => { + const element = assertElementExists('hasPrefixAvatar'); + const avatarElement = element.querySelector('.oss-infinite-select-option__prefix-avatar'); + const result = avatarElement !== null; + + QUnit.assert.pushResult({ + result, + actual: result, + expected: true, + message: message ?? 'InfiniteSelect::Option has a prefix avatar' + }); + }, + + doesNotHavePrefixAvatar: (message?: string) => { + const element = assertElementExists('doesNotHavePrefixAvatar'); + const avatarElement = element.querySelector('.oss-infinite-select-option__prefix-avatar'); + const result = avatarElement === null; + + QUnit.assert.pushResult({ + result, + actual: result, + expected: true, + message: message ?? 'InfiniteSelect::Option does not have a prefix avatar' + }); + }, + + hasPrefixBadge: (message?: string) => { + const element = assertElementExists('hasPrefixBadge'); + const badgeElement = element.querySelector('.oss-infinite-select-option__prefix-badge'); + const result = badgeElement !== null; + + QUnit.assert.pushResult({ + result, + actual: result, + expected: true, + message: message ?? 'InfiniteSelect::Option has a prefix badge' + }); + }, + + doesNotHavePrefixBadge: (message?: string) => { + const element = assertElementExists('doesNotHavePrefixBadge'); + const badgeElement = element.querySelector('.oss-infinite-select-option__prefix-badge'); + const result = badgeElement === null; + + QUnit.assert.pushResult({ + result, + actual: result, + expected: true, + message: message ?? 'InfiniteSelect::Option does not have a prefix badge' + }); + }, + + hasPrefixIcon: (message?: string) => { + const element = assertElementExists('hasPrefixIcon'); + const iconElement = element.querySelector('.oss-infinite-select-option__prefix-icon'); + const result = iconElement !== null; + + QUnit.assert.pushResult({ + result, + actual: result, + expected: true, + message: message ?? 'InfiniteSelect::Option has a prefix icon' + }); + }, + + doesNotHavePrefixIcon: (message?: string) => { + const element = assertElementExists('doesNotHavePrefixIcon'); + const iconElement = element.querySelector('.oss-infinite-select-option__prefix-icon'); + const result = iconElement === null; + + QUnit.assert.pushResult({ + result, + actual: result, + expected: true, + message: message ?? 'InfiniteSelect::Option does not have a prefix icon' + }); + }, + + hasPrefixCountry: (countryCode: string, message?: string) => { + const element = assertElementExists('hasPrefixCountry'); + const countryElement = element.querySelector('.oss-infinite-select-option__prefix-country'); + const result = countryElement?.classList.contains(`fflag-${countryCode}`) ?? false; + + QUnit.assert.pushResult({ + result, + actual: countryElement !== null ? countryCode : undefined, + expected: countryCode, + message: message ?? `InfiniteSelect::Option has prefix country "${countryCode}"` + }); + }, + + doesNotHavePrefixCountry: (message?: string) => { + const element = assertElementExists('doesNotHavePrefixCountry'); + const countryElement = element.querySelector('.oss-infinite-select-option__prefix-country'); + const result = countryElement === null; + + QUnit.assert.pushResult({ + result, + actual: result, + expected: true, + message: message ?? 'InfiniteSelect::Option does not have a prefix country' + }); + }, + + hasIcon: (message?: string) => { + const element = assertElementExists('hasIcon'); + const iconElement = element.querySelector('.oss-infinite-select-option__icon'); + const result = iconElement !== null; + + QUnit.assert.pushResult({ + result, + actual: result, + expected: true, + message: message ?? 'InfiniteSelect::Option has an icon' + }); + }, + + doesNotHaveIcon: (message?: string) => { + const element = assertElementExists('doesNotHaveIcon'); + const iconElement = element.querySelector('.oss-infinite-select-option__icon'); + const result = iconElement === null; + + QUnit.assert.pushResult({ + result, + actual: result, + expected: true, + message: message ?? 'InfiniteSelect::Option does not have an icon' + }); + }, + + hasSuffixHint: (hint: string, message?: string) => { + const element = assertElementExists('hasSuffixHint'); + const hintElement = element.querySelector('.oss-infinite-select-option__suffix-hint'); + const actual = hintElement ? (hintElement as HTMLElement).textContent?.trim() : ''; + const result = actual === hint; + + QUnit.assert.pushResult({ + result, + actual, + expected: hint, + message: message ?? `InfiniteSelect::Option has suffix hint "${hint}"` + }); + }, + + doesNotHaveSuffixHint: (message?: string) => { + const element = assertElementExists('doesNotHaveSuffixHint'); + const hintElement = element.querySelector('.oss-infinite-select-option__suffix-hint'); + const result = hintElement === null; + + QUnit.assert.pushResult({ + result, + actual: result, + expected: true, + message: message ?? 'InfiniteSelect::Option does not have a suffix hint' + }); + }, + + hasSuffixTag: (message?: string) => { + const element = assertElementExists('hasSuffixTag'); + const tagElement = element.querySelector('.oss-infinite-select-option__suffix-tag'); + const result = tagElement !== null; + + QUnit.assert.pushResult({ + result, + actual: result, + expected: true, + message: message ?? 'InfiniteSelect::Option has a suffix tag' + }); + }, + + doesNotHaveSuffixTag: (message?: string) => { + const element = assertElementExists('doesNotHaveSuffixTag'); + const tagElement = element.querySelector('.oss-infinite-select-option__suffix-tag'); + const result = tagElement === null; + + QUnit.assert.pushResult({ + result, + actual: result, + expected: true, + message: message ?? 'InfiniteSelect::Option does not have a suffix tag' + }); + }, + + hasSuffixIcon: (message?: string) => { + const element = assertElementExists('hasSuffixIcon'); + const iconElement = element.querySelector('.oss-infinite-select-option__suffix-icon'); + const result = iconElement !== null; + + QUnit.assert.pushResult({ + result, + actual: result, + expected: true, + message: message ?? 'InfiniteSelect::Option has a suffix icon' + }); + }, + + doesNotHaveSuffixIcon: (message?: string) => { + const element = assertElementExists('doesNotHaveSuffixIcon'); + const iconElement = element.querySelector('.oss-infinite-select-option__suffix-icon'); + const result = iconElement === null; + + QUnit.assert.pushResult({ + result, + actual: result, + expected: true, + message: message ?? 'InfiniteSelect::Option does not have a suffix icon' + }); + } + }; +}; + +assertion.__name__ = 'infiniteSelectOption'; + +export default assertion; + +declare global { + interface Assert { + infiniteSelectOption(_selector: string): InfiniteSelectOptionAssertions; + } +} diff --git a/addon-test-support/register-assertions.ts b/addon-test-support/register-assertions.ts index 4e92410e1..6a97c5cfd 100644 --- a/addon-test-support/register-assertions.ts +++ b/addon-test-support/register-assertions.ts @@ -1,8 +1,9 @@ import tooltipAssertions from '@upfluence/oss-components/test-support/custom-assertions/tooltip'; +import infiniteSelectOptionAssertions from '@upfluence/oss-components/test-support/custom-assertions/infinite-select-option'; -const ASSERTIONS = [tooltipAssertions]; +const ASSERTIONS = [tooltipAssertions, infiniteSelectOptionAssertions]; -export default function registerAssertions(assert: Assert) { +export default function registerAssertions(assert: any) { ASSERTIONS.forEach((assertion) => { // @ts-ignore assert[assertion.__name__] = assertion; diff --git a/tests/unit/custom-assertions/infinite-select-option-test.ts b/tests/unit/custom-assertions/infinite-select-option-test.ts new file mode 100644 index 000000000..839af96d9 --- /dev/null +++ b/tests/unit/custom-assertions/infinite-select-option-test.ts @@ -0,0 +1,288 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render, setupOnerror } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; +import sinon from 'sinon'; + +module('Test Support | Custom Assertions | infinite-select-option', function (hooks) { + setupRenderingTest(hooks); + + hooks.beforeEach(function () { + this.title = 'Test Option'; + this.onSelect = sinon.stub(); + }); + + module('.exists()', () => { + test('it passes when the option exists', async function (assert) { + await render(hbs``); + + assert.infiniteSelectOption('.oss-infinite-select-option').exists(); + }); + }); + + module('.doesNotExist()', () => { + test('it passes when the option does not exist', async function (assert) { + await render(hbs`
`); + + assert.infiniteSelectOption('.non-existent').doesNotExist(); + }); + }); + + module('.hasTitle()', () => { + test('it passes when the title matches', async function (assert) { + await render(hbs``); + + assert.infiniteSelectOption('.oss-infinite-select-option').hasTitle('My Title'); + }); + }); + + module('.hasSubtitle()', () => { + test('it passes when the subtitle matches', async function (assert) { + await render(hbs` + + `); + + assert.infiniteSelectOption('.oss-infinite-select-option').hasSubtitle('My Subtitle'); + }); + }); + + module('.doesNotHaveSubtitle()', () => { + test('it passes when there is no subtitle', async function (assert) { + await render(hbs``); + + assert.infiniteSelectOption('.oss-infinite-select-option').doesNotHaveSubtitle(); + }); + }); + + module('.isSelected()', () => { + test('it passes when the option is selected', async function (assert) { + await render(hbs` + + `); + + assert.infiniteSelectOption('.oss-infinite-select-option').isSelected(); + }); + }); + + module('.isNotSelected()', () => { + test('it passes when the option is not selected', async function (assert) { + await render(hbs` + + `); + + assert.infiniteSelectOption('.oss-infinite-select-option').isNotSelected(); + }); + }); + + module('.isDisabled()', () => { + test('it passes when the option is disabled', async function (assert) { + await render(hbs` + + `); + + assert.infiniteSelectOption('.oss-infinite-select-option').isDisabled(); + }); + }); + + module('.isNotDisabled()', () => { + test('it passes when the option is not disabled', async function (assert) { + await render(hbs` + + `); + + assert.infiniteSelectOption('.oss-infinite-select-option').isNotDisabled(); + }); + }); + + module('.hasSelectionType()', () => { + test('it passes when selectionType is single', async function (assert) { + await render(hbs` + + `); + + assert.infiniteSelectOption('.oss-infinite-select-option').hasSelectionType('single'); + }); + + test('it passes when selectionType is multiple', async function (assert) { + await render(hbs` + + `); + + assert.infiniteSelectOption('.oss-infinite-select-option').hasSelectionType('multiple'); + }); + }); + + module('.hasPrefixAvatar()', () => { + test('it passes when prefix avatar is present', async function (assert) { + this.prefixAvatar = { initials: 'JD' }; + await render(hbs` + + `); + + assert.infiniteSelectOption('.oss-infinite-select-option').hasPrefixAvatar(); + }); + }); + + module('.doesNotHavePrefixAvatar()', () => { + test('it passes when prefix avatar is not present', async function (assert) { + await render(hbs``); + + assert.infiniteSelectOption('.oss-infinite-select-option').doesNotHavePrefixAvatar(); + }); + }); + + module('.hasPrefixBadge()', () => { + test('it passes when prefix badge is present', async function (assert) { + this.prefixBadge = { icon: 'fa-star' }; + await render(hbs` + + `); + + assert.infiniteSelectOption('.oss-infinite-select-option').hasPrefixBadge(); + }); + }); + + module('.hasPrefixIcon()', () => { + test('it passes when prefix icon is present', async function (assert) { + this.prefixIcon = { icon: 'fa-star' }; + await render(hbs` + + `); + + assert.infiniteSelectOption('.oss-infinite-select-option').hasPrefixIcon(); + }); + }); + + module('.hasPrefixCountry()', () => { + test('it passes when prefix country matches', async function (assert) { + await render(hbs` + + `); + + assert.infiniteSelectOption('.oss-infinite-select-option').hasPrefixCountry('US'); + }); + }); + + module('.hasIcon()', () => { + test('it passes when icon is present', async function (assert) { + this.icon = { icon: 'fa-info-circle' }; + await render(hbs` + + `); + + assert.infiniteSelectOption('.oss-infinite-select-option').hasIcon(); + }); + }); + + module('.hasSuffixHint()', () => { + test('it passes when suffix hint matches', async function (assert) { + await render(hbs` + + `); + + assert.infiniteSelectOption('.oss-infinite-select-option').hasSuffixHint('+5'); + }); + }); + + module('.hasSuffixTag()', () => { + test('it passes when suffix tag is present', async function (assert) { + this.suffixTag = { label: 'Premium' }; + await render(hbs` + + `); + + assert.infiniteSelectOption('.oss-infinite-select-option').hasSuffixTag(); + }); + }); + + module('.hasSuffixIcon()', () => { + test('it passes when suffix icon is present', async function (assert) { + this.suffixIcon = { icon: 'fa-chevron-right' }; + await render(hbs` + + `); + + assert.infiniteSelectOption('.oss-infinite-select-option').hasSuffixIcon(); + }); + }); + + module('chaining assertions', () => { + test('it supports multiple assertions on the same element', async function (assert) { + await render(hbs` + + `); + + assert.infiniteSelectOption('.oss-infinite-select-option').exists(); + assert.infiniteSelectOption('.oss-infinite-select-option').hasTitle('Complex Option'); + assert.infiniteSelectOption('.oss-infinite-select-option').hasSubtitle('With subtitle'); + assert.infiniteSelectOption('.oss-infinite-select-option').isSelected(); + assert.infiniteSelectOption('.oss-infinite-select-option').hasSelectionType('single'); + }); + }); +}); From ca22e2f32626aba1d4cafa2034b9201a8e9cd72f Mon Sep 17 00:00:00 2001 From: Julien Vannier Date: Tue, 3 Mar 2026 10:39:28 +0100 Subject: [PATCH 2/2] PR feedback --- .../infinite-select-option.ts | 54 +++++++++---------- .../custom-assertions/tooltip.ts | 40 +++++++------- addon-test-support/register-assertions.ts | 2 +- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/addon-test-support/custom-assertions/infinite-select-option.ts b/addon-test-support/custom-assertions/infinite-select-option.ts index 48339d108..7fe793392 100644 --- a/addon-test-support/custom-assertions/infinite-select-option.ts +++ b/addon-test-support/custom-assertions/infinite-select-option.ts @@ -32,7 +32,7 @@ export interface InfiniteSelectOptionAssertions { doesNotHaveSuffixIcon(_message?: string): void; } -const assertion = (selector: string) => { +const assertion = (selector: string): InfiniteSelectOptionAssertions => { const getElement = (): Element | null => { return document.querySelector(selector); }; @@ -48,7 +48,7 @@ const assertion = (selector: string) => { }; return { - exists: (message?: string) => { + exists: (message?: string): void => { const element = getElement(); const result = element !== null && element.classList.contains('oss-infinite-select-option'); @@ -60,7 +60,7 @@ const assertion = (selector: string) => { }); }, - doesNotExist: (message?: string) => { + doesNotExist: (message?: string): void => { const element = getElement(); const result = element === null || !element.classList.contains('oss-infinite-select-option'); @@ -72,7 +72,7 @@ const assertion = (selector: string) => { }); }, - hasTitle: (title: string, message?: string) => { + hasTitle: (title: string, message?: string): void => { const element = assertElementExists('hasTitle'); const titleElement = element.querySelector('.oss-infinite-select-option__title'); const actual = titleElement ? (titleElement as HTMLElement).textContent?.trim() : ''; @@ -86,7 +86,7 @@ const assertion = (selector: string) => { }); }, - hasSubtitle: (subtitle: string, message?: string) => { + hasSubtitle: (subtitle: string, message?: string): void => { const element = assertElementExists('hasSubtitle'); const subtitleElement = element.querySelector('.oss-infinite-select-option__subtitle'); const actual = subtitleElement ? (subtitleElement as HTMLElement).textContent?.trim() : ''; @@ -100,7 +100,7 @@ const assertion = (selector: string) => { }); }, - doesNotHaveSubtitle: (message?: string) => { + doesNotHaveSubtitle: (message?: string): void => { const element = assertElementExists('doesNotHaveSubtitle'); const subtitleElement = element.querySelector('.oss-infinite-select-option__subtitle'); const actual = subtitleElement ? (subtitleElement as HTMLElement).textContent?.trim() : undefined; @@ -114,7 +114,7 @@ const assertion = (selector: string) => { }); }, - isSelected: (message?: string) => { + isSelected: (message?: string): void => { const element = assertElementExists('isSelected'); const result = element.classList.contains('oss-infinite-select-option--selected'); @@ -126,7 +126,7 @@ const assertion = (selector: string) => { }); }, - isNotSelected: (message?: string) => { + isNotSelected: (message?: string): void => { const element = assertElementExists('isNotSelected'); const result = !element.classList.contains('oss-infinite-select-option--selected'); @@ -138,7 +138,7 @@ const assertion = (selector: string) => { }); }, - isDisabled: (message?: string) => { + isDisabled: (message?: string): void => { const element = assertElementExists('isDisabled'); const result = element.classList.contains('oss-infinite-select-option--disabled'); @@ -150,7 +150,7 @@ const assertion = (selector: string) => { }); }, - isNotDisabled: (message?: string) => { + isNotDisabled: (message?: string): void => { const element = assertElementExists('isNotDisabled'); const result = !element.classList.contains('oss-infinite-select-option--disabled'); @@ -162,7 +162,7 @@ const assertion = (selector: string) => { }); }, - hasSelectionType: (selectionType: SelectionType, message?: string) => { + hasSelectionType: (selectionType: SelectionType, message?: string): void => { const element = assertElementExists('hasSelectionType'); const type = selectionType ?? 'single'; const result = element.classList.contains(`oss-infinite-select-option--${type}`); @@ -179,7 +179,7 @@ const assertion = (selector: string) => { }); }, - hasPrefixAvatar: (message?: string) => { + hasPrefixAvatar: (message?: string): void => { const element = assertElementExists('hasPrefixAvatar'); const avatarElement = element.querySelector('.oss-infinite-select-option__prefix-avatar'); const result = avatarElement !== null; @@ -192,7 +192,7 @@ const assertion = (selector: string) => { }); }, - doesNotHavePrefixAvatar: (message?: string) => { + doesNotHavePrefixAvatar: (message?: string): void => { const element = assertElementExists('doesNotHavePrefixAvatar'); const avatarElement = element.querySelector('.oss-infinite-select-option__prefix-avatar'); const result = avatarElement === null; @@ -205,7 +205,7 @@ const assertion = (selector: string) => { }); }, - hasPrefixBadge: (message?: string) => { + hasPrefixBadge: (message?: string): void => { const element = assertElementExists('hasPrefixBadge'); const badgeElement = element.querySelector('.oss-infinite-select-option__prefix-badge'); const result = badgeElement !== null; @@ -218,7 +218,7 @@ const assertion = (selector: string) => { }); }, - doesNotHavePrefixBadge: (message?: string) => { + doesNotHavePrefixBadge: (message?: string): void => { const element = assertElementExists('doesNotHavePrefixBadge'); const badgeElement = element.querySelector('.oss-infinite-select-option__prefix-badge'); const result = badgeElement === null; @@ -231,7 +231,7 @@ const assertion = (selector: string) => { }); }, - hasPrefixIcon: (message?: string) => { + hasPrefixIcon: (message?: string): void => { const element = assertElementExists('hasPrefixIcon'); const iconElement = element.querySelector('.oss-infinite-select-option__prefix-icon'); const result = iconElement !== null; @@ -244,7 +244,7 @@ const assertion = (selector: string) => { }); }, - doesNotHavePrefixIcon: (message?: string) => { + doesNotHavePrefixIcon: (message?: string): void => { const element = assertElementExists('doesNotHavePrefixIcon'); const iconElement = element.querySelector('.oss-infinite-select-option__prefix-icon'); const result = iconElement === null; @@ -257,7 +257,7 @@ const assertion = (selector: string) => { }); }, - hasPrefixCountry: (countryCode: string, message?: string) => { + hasPrefixCountry: (countryCode: string, message?: string): void => { const element = assertElementExists('hasPrefixCountry'); const countryElement = element.querySelector('.oss-infinite-select-option__prefix-country'); const result = countryElement?.classList.contains(`fflag-${countryCode}`) ?? false; @@ -270,7 +270,7 @@ const assertion = (selector: string) => { }); }, - doesNotHavePrefixCountry: (message?: string) => { + doesNotHavePrefixCountry: (message?: string): void => { const element = assertElementExists('doesNotHavePrefixCountry'); const countryElement = element.querySelector('.oss-infinite-select-option__prefix-country'); const result = countryElement === null; @@ -283,7 +283,7 @@ const assertion = (selector: string) => { }); }, - hasIcon: (message?: string) => { + hasIcon: (message?: string): void => { const element = assertElementExists('hasIcon'); const iconElement = element.querySelector('.oss-infinite-select-option__icon'); const result = iconElement !== null; @@ -296,7 +296,7 @@ const assertion = (selector: string) => { }); }, - doesNotHaveIcon: (message?: string) => { + doesNotHaveIcon: (message?: string): void => { const element = assertElementExists('doesNotHaveIcon'); const iconElement = element.querySelector('.oss-infinite-select-option__icon'); const result = iconElement === null; @@ -309,7 +309,7 @@ const assertion = (selector: string) => { }); }, - hasSuffixHint: (hint: string, message?: string) => { + hasSuffixHint: (hint: string, message?: string): void => { const element = assertElementExists('hasSuffixHint'); const hintElement = element.querySelector('.oss-infinite-select-option__suffix-hint'); const actual = hintElement ? (hintElement as HTMLElement).textContent?.trim() : ''; @@ -323,7 +323,7 @@ const assertion = (selector: string) => { }); }, - doesNotHaveSuffixHint: (message?: string) => { + doesNotHaveSuffixHint: (message?: string): void => { const element = assertElementExists('doesNotHaveSuffixHint'); const hintElement = element.querySelector('.oss-infinite-select-option__suffix-hint'); const result = hintElement === null; @@ -336,7 +336,7 @@ const assertion = (selector: string) => { }); }, - hasSuffixTag: (message?: string) => { + hasSuffixTag: (message?: string): void => { const element = assertElementExists('hasSuffixTag'); const tagElement = element.querySelector('.oss-infinite-select-option__suffix-tag'); const result = tagElement !== null; @@ -349,7 +349,7 @@ const assertion = (selector: string) => { }); }, - doesNotHaveSuffixTag: (message?: string) => { + doesNotHaveSuffixTag: (message?: string): void => { const element = assertElementExists('doesNotHaveSuffixTag'); const tagElement = element.querySelector('.oss-infinite-select-option__suffix-tag'); const result = tagElement === null; @@ -362,7 +362,7 @@ const assertion = (selector: string) => { }); }, - hasSuffixIcon: (message?: string) => { + hasSuffixIcon: (message?: string): void => { const element = assertElementExists('hasSuffixIcon'); const iconElement = element.querySelector('.oss-infinite-select-option__suffix-icon'); const result = iconElement !== null; @@ -375,7 +375,7 @@ const assertion = (selector: string) => { }); }, - doesNotHaveSuffixIcon: (message?: string) => { + doesNotHaveSuffixIcon: (message?: string): void => { const element = assertElementExists('doesNotHaveSuffixIcon'); const iconElement = element.querySelector('.oss-infinite-select-option__suffix-icon'); const result = iconElement === null; diff --git a/addon-test-support/custom-assertions/tooltip.ts b/addon-test-support/custom-assertions/tooltip.ts index 3ff220088..019282125 100644 --- a/addon-test-support/custom-assertions/tooltip.ts +++ b/addon-test-support/custom-assertions/tooltip.ts @@ -28,21 +28,21 @@ async function triggerEventOnElement(selector: string, trigger: string = 'mouseo } export interface TooltipAssertions { - exists(trigger?: string, message?: string): void; - doesNotExist(trigger?: string, message?: string): void; - hasTitle(title: string, message?: string): void; - hasSubtitle(subtitle: string, message?: string): void; - doesNotHaveSubtitle(message?: string): void; - hasIcon(icon: string, message?: string): void; - doesNotHaveIcon(message?: string): void; - hasPlacement(placement: Placement, message?: string): void; + exists(trigger?: string, message?: string): Promise; + doesNotExist(trigger?: string, message?: string): Promise; + hasTitle(title: string, message?: string): Promise; + hasSubtitle(subtitle: string, message?: string): Promise; + doesNotHaveSubtitle(message?: string): Promise; + hasIcon(icon: string, message?: string): Promise; + doesNotHaveIcon(message?: string): Promise; + hasPlacement(placement: Placement, message?: string): Promise; isHtmlSafe(message?: string): void; - isNotHtmlSafe(message?: string): void; + isNotHtmlSafe(message?: string): Promise; } -const assertion = (selector: string) => { +const assertion = (selector: string): TooltipAssertions => { return { - exists: async (trigger?: string, message?: string) => { + exists: async (trigger?: string, message?: string): Promise => { let result: boolean = true; let actual: Element | null = null; @@ -56,7 +56,7 @@ const assertion = (selector: string) => { }); }, - doesNotExist: async (trigger: string = 'mouseover', message?: string) => { + doesNotExist: async (trigger: string = 'mouseover', message?: string): Promise => { let result: boolean = false; let actual: Element | null = null; const existingClock = sinon.clock; @@ -88,7 +88,7 @@ const assertion = (selector: string) => { }); }, - hasTitle: async (title: string, message?: string) => { + hasTitle: async (title: string, message?: string): Promise => { await triggerEventOnElement(selector); let result: boolean = false; @@ -108,7 +108,7 @@ const assertion = (selector: string) => { }); }, - hasSubtitle: async (subtitle: string, message?: string) => { + hasSubtitle: async (subtitle: string, message?: string): Promise => { await triggerEventOnElement(selector); let result: boolean = false; @@ -128,7 +128,7 @@ const assertion = (selector: string) => { }); }, - doesNotHaveSubtitle: async (message?: string) => { + doesNotHaveSubtitle: async (message?: string): Promise => { await triggerEventOnElement(selector); let result = false; @@ -146,7 +146,7 @@ const assertion = (selector: string) => { }); }, - hasIcon: async (icon: string, message?: string) => { + hasIcon: async (icon: string, message?: string): Promise => { await triggerEventOnElement(selector); let result: boolean = false; @@ -166,7 +166,7 @@ const assertion = (selector: string) => { }); }, - doesNotHaveIcon: async (message?: string) => { + doesNotHaveIcon: async (message?: string): Promise => { await triggerEventOnElement(selector); const iconI = document.querySelector('.upf-tooltip .title-container i'); @@ -181,7 +181,7 @@ const assertion = (selector: string) => { }); }, - hasPlacement: async (placement: Placement, message?: string) => { + hasPlacement: async (placement: Placement, message?: string): Promise => { await triggerEventOnElement(selector); let result: boolean = false; @@ -201,7 +201,7 @@ const assertion = (selector: string) => { }); }, - isHtmlSafe: async (message?: string) => { + isHtmlSafe: async (message?: string): Promise => { await triggerEventOnElement(selector); const titleContainer = document.querySelector('.upf-tooltip .title-container .title'); @@ -216,7 +216,7 @@ const assertion = (selector: string) => { }); }, - isNotHtmlSafe: async (message?: string) => { + isNotHtmlSafe: async (message?: string): Promise => { await triggerEventOnElement(selector); const titleContainer = document.querySelector('.upf-tooltip .title-container .title'); diff --git a/addon-test-support/register-assertions.ts b/addon-test-support/register-assertions.ts index 6a97c5cfd..ff0fec666 100644 --- a/addon-test-support/register-assertions.ts +++ b/addon-test-support/register-assertions.ts @@ -3,7 +3,7 @@ import infiniteSelectOptionAssertions from '@upfluence/oss-components/test-suppo const ASSERTIONS = [tooltipAssertions, infiniteSelectOptionAssertions]; -export default function registerAssertions(assert: any) { +export default function registerAssertions(assert: Assert) { ASSERTIONS.forEach((assertion) => { // @ts-ignore assert[assertion.__name__] = assertion;