Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Carousel: Cypress component tests #33231

Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { baseConfig } from '@fluentui/scripts-cypress';
Mitch-At-Work marked this conversation as resolved.
Show resolved Hide resolved

export default baseConfig;
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
"@fluentui/eslint-plugin": "*",
"@fluentui/react-conformance": "*",
"@fluentui/react-conformance-griffel": "*",
"@fluentui/scripts-api-extractor": "*"
"@fluentui/scripts-api-extractor": "*",
"@fluentui/scripts-cypress": "*"
},
"dependencies": {
"@fluentui/react-aria": "^9.13.8",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import 'cypress-real-events';
import * as React from 'react';
import { mount as mountBase } from '@cypress/react';
import { FluentProvider } from '@fluentui/react-provider';
import { teamsLightTheme } from '@fluentui/react-theme';
import type { CarouselProps } from './Carousel.types';
import { CarouselNav } from '../CarouselNav/CarouselNav';
import { CarouselNavButton, carouselNavButtonClassNames } from '../CarouselNavButton/index';
import { CarouselNavContainer } from '../CarouselNavContainer/CarouselNavContainer';
import { CarouselSlider } from '../CarouselSlider/CarouselSlider';
import { CarouselViewport } from '../CarouselViewport/CarouselViewport';
import { Carousel } from './Carousel';
import { CarouselCard, carouselCardClassNames } from '../CarouselCard/index';

const mount = (element: JSX.Element) => {
mountBase(<FluentProvider theme={teamsLightTheme}>{element}</FluentProvider>);
};

const CarouselTest: React.FC<CarouselProps> = props => {
return (
<Carousel {...props}>
<CarouselViewport>
<CarouselSlider cardFocus>
<CarouselCard>Card 1</CarouselCard>
<CarouselCard>Card 2</CarouselCard>
<CarouselCard>Card 3</CarouselCard>
</CarouselSlider>
</CarouselViewport>
<CarouselNavContainer>
<CarouselNav>{index => <CarouselNavButton aria-label={`Carousel Nav Button ${index}`} />}</CarouselNav>
</CarouselNavContainer>
</Carousel>
);
};
CarouselTest.displayName = 'CarouselTest';

describe('CarouselControlledIndexTest', () => {
it('Should render to initial value', () => {
const defaultActiveIndex = 1;
mount(<CarouselTest defaultActiveIndex={defaultActiveIndex} />);
const activeIndexNavButton = cy.get<HTMLElement>(`.${carouselNavButtonClassNames.root}`).eq(defaultActiveIndex);
activeIndexNavButton.should('have.attr', 'aria-selected', 'true');
});

it('Should render to controlled value', () => {
const controlledActiveIndex = 1;
mount(<CarouselTest activeIndex={controlledActiveIndex} />);
const activeIndexNavButton = cy.get<HTMLElement>(`.${carouselNavButtonClassNames.root}`).eq(controlledActiveIndex);
activeIndexNavButton.should('have.attr', 'aria-selected', 'true');
});

it('Should callback new index value', () => {
const controlledActiveIndex = 1;
let callbackIndex = controlledActiveIndex;
const callback = (ev, data) => {
callbackIndex = data.index;
};
mount(<CarouselTest defaultActiveIndex={controlledActiveIndex} onActiveIndexChange={callback} />);

const prevIndexButton = cy.get<HTMLElement>(`.${carouselNavButtonClassNames.root}`).first();
const nextIndexButton = cy.get<HTMLElement>(`.${carouselNavButtonClassNames.root}`).last();

// Click to previous of original index
prevIndexButton.click().then(() => {
expect(callbackIndex).equals(controlledActiveIndex - 1);
const prevIndexNavButton = cy
.get<HTMLElement>(`.${carouselNavButtonClassNames.root}`)
.eq(controlledActiveIndex - 1);
prevIndexNavButton.should('have.attr', 'aria-selected', 'true');
});

// Check next from original index
nextIndexButton.click().then(() => {
expect(callbackIndex).equals(controlledActiveIndex + 1);
const nextIndexNavButton = cy
.get<HTMLElement>(`.${carouselNavButtonClassNames.root}`)
.eq(controlledActiveIndex + 1);
nextIndexNavButton.should('have.attr', 'aria-selected', 'true');
});
});

it('Should set index to focused card', () => {
mount(<CarouselTest />);

const secondCard = cy.get<HTMLElement>(`.${carouselCardClassNames.root}`).eq(1);
secondCard.focus().then(() => {
const nextIndexNavButton = cy.get<HTMLElement>(`.${carouselNavButtonClassNames.root}`).eq(1);
nextIndexNavButton.should('have.attr', 'aria-selected', 'true');
});
});
});

const CarouselAddCardsTest: React.FC<CarouselProps> = props => {
const [isVisible, setVisibility] = React.useState(false);
const [controlledIndex, setControlledIndex] = React.useState<number | undefined>(props.activeIndex);

return (
<Carousel {...props} activeIndex={controlledIndex}>
<CarouselViewport>
<CarouselSlider>
<CarouselCard>Card 1</CarouselCard>
<CarouselCard>Card 2</CarouselCard>
<CarouselCard>Card 3</CarouselCard>
{isVisible && <CarouselCard>Card 4</CarouselCard>}
{isVisible && <CarouselCard>Card 5</CarouselCard>}
</CarouselSlider>
</CarouselViewport>
<CarouselNavContainer>
<CarouselNav>{index => <CarouselNavButton aria-label={`Carousel Nav Button ${index}`} />}</CarouselNav>
</CarouselNavContainer>
<button
className="addNewCardsButton"
onClick={() => {
// Add and set to new card
setVisibility(true);
setControlledIndex(4);
}}
>
{'Add cards'}
</button>
</Carousel>
);
};
CarouselAddCardsTest.displayName = 'CarouselAddCardsTest';

describe('CarouselAddCardsTest', () => {
it('Should handle controlled index set to newly appended card', () => {
mount(<CarouselAddCardsTest activeIndex={1} />);

const activeIndexNavButton = cy.get<HTMLElement>(`.${carouselNavButtonClassNames.root}`).eq(1);
activeIndexNavButton.should('have.attr', 'aria-selected', 'true');
cy.then(() => {
const addNewCardsButton = cy.get<HTMLElement>('.addNewCardsButton');
addNewCardsButton.click();
}).then(() => {
const newCardIndexNavButton = cy.get<HTMLElement>(`.${carouselNavButtonClassNames.root}`).eq(4);
newCardIndexNavButton.should('have.attr', 'aria-selected', 'true');
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"isolatedModules": false,
"types": ["node", "cypress", "cypress-real-events"],
"typeRoots": ["../../../../../node_modules", "../../../../../node_modules/@types"],
"lib": ["ES2019", "dom"]
},
"include": ["**/*.cy.ts", "**/*.cy.tsx"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
},
{
"path": "./tsconfig.spec.json"
},
{
"path": "./tsconfig.cy.json"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@
"**/*.test.ts",
"**/*.test.tsx",
"**/*.stories.ts",
"**/*.stories.tsx"
"**/*.stories.tsx",
"**/*.cy.ts",
"**/*.cy.tsx"
],
"include": ["./src/**/*.ts", "./src/**/*.tsx"]
}
Loading