Skip to content

Commit

Permalink
feat(components, documentation): e2e tests for rating component
Browse files Browse the repository at this point in the history
  • Loading branch information
davidritter-dotcom committed Dec 21, 2023
1 parent c0dcbac commit 59595cc
Show file tree
Hide file tree
Showing 12 changed files with 269 additions and 15 deletions.
4 changes: 2 additions & 2 deletions packages/components-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
"linkDirectory": true
},
"scripts": {
"build": "npm run clean && npm run compile",
"build": "pnpm run clean && pnpm run compile",
"clean": "rimraf dist",
"compile": "npm run tsc",
"compile": "pnpm run tsc",
"tsc": "tsc -p ."
},
"dependencies": {
Expand Down
217 changes: 217 additions & 0 deletions packages/components/cypress/e2e/rating.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
describe('rating', () => {
describe('default', () => {
beforeEach(() => {
cy.getComponent('post-rating');
cy.get('@rating').get('div.rating').as('rating-container');
cy.get('@rating-container').get('svg').as('stars');
});

it('should render', () => {
cy.get('@rating').should('exist');
});

it('should display 10 empty stars', () => {
cy.get('@stars').then($stars => {
expect($stars.length).to.equal(10);

$stars.each((_index, star) => {
const classes = Cypress.$(star).attr('class').split(' ');
expect(classes).to.have.length(1);
expect(classes[0]).to.equal('star');
});
});
});

it('should set correct rating by clicking on a star', () => {
for (let i = 0; i < 10; i++) {
cy.get('@stars').eq(i).click();
cy.get('@stars').each(($star, index) => {
if (index <= i) {
cy.wrap($star).should('have.class', 'active-star');
} else {
cy.wrap($star).should('not.have.class', 'active-star');
}
});
}
});

// The hover test does not yet work, need to find a way to make assertions while hovering

// it('should change appearance on hover', () => {
// cy.get('@stars').eq(3).trigger('mouseover');
// cy.get('@stars').each(($star, index) => {
// if (index <= 3) {
// cy.wrap($star).should('have.class', 'hovered-star');
// } else {
// cy.wrap($star).should('not.have.class', 'hovered-star');
// }
// });
// });

it('should check if stars reflect current-rating attribute correctly', () => {
for (let rating = 1; rating <= 10; rating++) {
cy.get('@rating').invoke('attr', 'current-rating', `${rating}`);

cy.get('@stars').each(($star, index) => {
if (index < rating) {
cy.wrap($star).should('have.class', 'active-star');
} else {
cy.wrap($star).should('not.have.class', 'active-star');
}
});
}
});

it('should render the correct number of stars based on max attribute', () => {
cy.get('@rating').invoke('attr', 'max', 7);
cy.get('@stars').should('have.length', 7);
});

// The keyboard test does not yet work because of the focus

// it('should navigate using keyboard arrows and confirm selection with Enter', () => {
// cy.get('@rating-container')
// .focus()
// .type('{rightarrow}{rightarrow}{rightarrow}{rightarrow}{enter}');
// cy.get('@stars').each(($star, index) => {
// if (index < 4) {
// cy.wrap($star).should('have.class', 'active-star');
// } else {
// cy.wrap($star).should('not.have.class', 'active-star');
// }
// });
// });

it('should disable interaction when disabled', () => {
cy.get('@rating').invoke('attr', 'disabled', true);
cy.get('@stars').each(($star, index) => {
cy.wrap($star).should('have.class', 'default-disabled');

cy.wrap($star)
.click()
.then(() => {
if (index === 0) {
cy.wrap($star).should('not.have.class', 'active-star');
} else {
for (let i = 0; i <= index; i++) {
cy.get('@stars').eq(i).should('not.have.class', 'active-star');
}
}
});
});
});

it('should maintain default active stars in disabled state', () => {
const defaultRating = 3;
cy.get('@rating').invoke('attr', 'current-rating', defaultRating);

cy.get('@rating').invoke('attr', 'disabled', true);

cy.get('@stars').each(($star, index) => {
if (index < defaultRating) {
cy.wrap($star).should('have.class', 'active-disabled');
} else {
cy.wrap($star).should('have.class', 'default-disabled');
cy.wrap($star).should('not.have.class', 'active-disabled');
}

cy.wrap($star)
.click()
.then(() => {
if (index < defaultRating) {
cy.wrap($star).should('have.class', 'active-disabled');
cy.wrap($star).should('not.have.class', 'active-star');
} else {
cy.wrap($star).should('have.class', 'default-disabled');
cy.wrap($star).should('not.have.class', 'active-disabled');
}
});
});
});

it('should disable interaction when readonly', () => {
cy.get('@rating').invoke('attr', 'readonly', true);
cy.get('@stars').each(($star, index) => {
const classes = Cypress.$($star).attr('class').split(' ');
expect(classes).to.have.length(1);
expect(classes[0]).to.equal('star');

cy.wrap($star)
.click()
.then(() => {
if (index === 0) {
cy.wrap($star).should('not.have.class', 'active-star');
} else {
for (let i = 0; i <= index; i++) {
cy.get('@stars').eq(i).should('not.have.class', 'active-star');
}
}
});
});
});

it('should maintain default active stars in readonly state', () => {
const defaultRating = 3;
cy.get('@rating').invoke('attr', 'current-rating', defaultRating);

cy.get('@rating').invoke('attr', 'readonly', true);

cy.get('@stars').each(($star, index) => {
if (index < defaultRating) {
cy.wrap($star).should('have.class', 'active-star');
} else {
cy.wrap($star).should('not.have.class', 'active-star');
}

cy.wrap($star)
.click()
.then(() => {
if (index < defaultRating) {
cy.wrap($star).should('have.class', 'active-star');
} else {
cy.wrap($star).should('not.have.class', 'active-star');
}
});
});
});

it('should have correct ARIA attributes', () => {
const defaultRating = 3;
const max = 8;

cy.get('@rating').invoke('attr', 'current-rating', defaultRating);

cy.get('@rating').invoke('attr', 'disabled', true);

cy.get('@rating').invoke('attr', 'max', max);

// Check ARIA attributes
cy.get('@rating-container').should('have.attr', 'role', 'slider');
cy.get('@rating-container').should('have.attr', 'aria-valuemin', '0');
cy.get('@rating-container').should('have.attr', 'aria-valuemax', `${max}`);
cy.get('@rating-container').should('have.attr', 'aria-valuenow', `${defaultRating}`);
cy.get('@rating-container').should(
'have.attr',
'aria-valuetext',
`${defaultRating} out of ${max}`,
);
cy.get('@rating-container').should('have.attr', 'aria-readonly', 'false');
cy.get('@rating-container').should('have.attr', 'aria-disabled', 'true');
});

it('should emit ratingChanged event when rating changes', () => {
cy.get('@rating').then($rating => {
const listener = cy.stub();
$rating[0].addEventListener('ratingChanged', listener);

const newRating = 5;
cy.get('@stars')
.eq(newRating - 1)
.click()
.then(() => {
cy.wrap(listener).should('be.calledWithMatch', { detail: newRating });
});
});
});
});
});
2 changes: 1 addition & 1 deletion packages/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"start": "stencil build --watch --docs-readme",
"build": "stencil build --docs-readme",
"clean": "rimraf www dist loader",
"test": "npm run unit",
"test": "pnpm run unit",
"unit": "stencil test --spec",
"unit:watch": "stencil test --spec --watchAll --silent",
"e2e": "cypress run",
Expand Down
8 changes: 4 additions & 4 deletions packages/components/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,15 +111,15 @@ export namespace Components {
*/
"currentRating": number;
/**
* The number of stars in the rating
* Boolean for the disabled state of the component
*/
"disabled"?: boolean;
/**
* The number of stars in the rating
*/
"max"?: number;
/**
* The number of stars in the rating
* If readonly is true, the component only displays a rating and is not interactive.
*/
"readonly"?: boolean;
}
Expand Down Expand Up @@ -387,7 +387,7 @@ declare namespace LocalJSX {
*/
"currentRating"?: number;
/**
* The number of stars in the rating
* Boolean for the disabled state of the component
*/
"disabled"?: boolean;
/**
Expand All @@ -399,7 +399,7 @@ declare namespace LocalJSX {
*/
"onRatingChanged"?: (event: PostRatingCustomEvent<number>) => void;
/**
* The number of stars in the rating
* If readonly is true, the component only displays a rating and is not interactive.
*/
"readonly"?: boolean;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export class PostRating {
onClick={this.isInteractive() && (() => this.handleClick(i))}
onMouseEnter={this.isInteractive() && (() => this.handleHover(i))}
onMouseLeave={this.isInteractive() && (() => this.reset())}
cursor={this.isInteractive() && 'pointer'}
cursor={this.isInteractive() ? 'pointer' : 'default'}
>
<path d="M15.2047 8.01289L15.3173 8.25303L15.5793 8.29449L22.981 9.46594L17.8102 14.8955L17.6402 15.0741L17.6783 15.3177L18.8923 23.0722L12.3555 19.4823L12.1149 19.3501L11.8742 19.4823L5.3374 23.0722L6.55141 15.3177L6.59048 15.0681L6.4128 14.8886L1.0518 9.47202L8.30071 8.41511L8.56513 8.37655L8.68001 8.1353L11.9965 1.17035L15.2047 8.01289Z" />
</svg>,
Expand Down
12 changes: 6 additions & 6 deletions packages/components/src/components/post-rating/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@

## Properties

| Property | Attribute | Description | Type | Default |
| --------------- | ---------------- | --------------------------------- | --------- | ----------- |
| `currentRating` | `current-rating` | The current rating value | `number` | `undefined` |
| `disabled` | `disabled` | The number of stars in the rating | `boolean` | `false` |
| `max` | `max` | The number of stars in the rating | `number` | `10` |
| `readonly` | `readonly` | The number of stars in the rating | `boolean` | `false` |
| Property | Attribute | Description | Type | Default |
| --------------- | ---------------- | --------------------------------------------------------------------------------- | --------- | ----------- |
| `currentRating` | `current-rating` | The current rating value | `number` | `undefined` |
| `disabled` | `disabled` | Boolean for the disabled state of the component | `boolean` | `false` |
| `max` | `max` | The number of stars in the rating | `number` | `10` |
| `readonly` | `readonly` | If readonly is true, the component only displays a rating and is not interactive. | `boolean` | `false` |


## Events
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Canvas, Controls, Meta, Source } from '@storybook/blocks';
import LinkTo from '@storybook/addon-links/react';
import * as PostRatingStories from './post-rating.stories';

<Meta of={PostRatingStories} />

# Rating

<Canvas of={PostRatingStories.Default} />


Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Meta, StoryObj } from '@storybook/web-components';
import { html } from 'lit';
import { BADGE } from '../../../../.storybook/constants';

const meta: Meta<HTMLPostAlertElement> = {
title: 'Components/Post Rating',
component: 'post-rating',
render: render,
parameters: {
badges: [BADGE.NEEDS_REVISION],
},
};

export default meta;

// RENDERER
function render() {
return html`
<post-rating></post-rating>
`;
}

// STORIES
type Story = StoryObj<HTMLPostAlertElement>;

export const Default: Story = {};
Empty file.
Empty file.
Empty file.
2 changes: 1 addition & 1 deletion packages/internet-header/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
"start": "stencil build --watch --docs-readme",
"build": "stencil build --docs-readme",
"clean": "rimraf www dist loader",
"test": "npm run unit",
"test": "pnpm run unit",
"unit": "jest",
"unit:watch": "jest --watch",
"e2e": "cypress run",
Expand Down

0 comments on commit 59595cc

Please sign in to comment.