Skip to content

Commit

Permalink
test(egress-composite): screenshot/snapshot Playwright setup (#1023)
Browse files Browse the repository at this point in the history
  • Loading branch information
arnautov-anton authored Sep 20, 2023
1 parent bfe7bea commit da140cb
Show file tree
Hide file tree
Showing 24 changed files with 666 additions and 117 deletions.
63 changes: 63 additions & 0 deletions .github/workflows/egress-composite-e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: Egress Composite E2E
on:
pull_request:
types:
- opened
- synchronize
- reopened
paths:
- 'sample-apps/react/egress-composite/**'
- 'packages/client/**'
- 'packages/react-sdk/**'
- 'packages/styling/**'
- 'packages/react-bindings/**'
env:
VITE_STREAM_API_KEY: ${{ vars.EGRESS_STREAM_API_KEY }}
VITE_STREAM_USER_TOKEN: ${{ secrets.EGRESS_USER_TOKEN }}
STREAM_SDK_TEST_APP: ${{ secrets.STREAM_SDK_TEST_APP }}
STREAM_SDK_TEST_ACCOUNT_EMAIL: ${{ secrets.STREAM_SDK_TEST_ACCOUNT_EMAIL }}
STREAM_SDK_TEST_ACCOUNT_PASSWORD: ${{ secrets.STREAM_SDK_TEST_ACCOUNT_PASSWORD }}
STREAM_SDK_TEST_ACCOUNT_OTP_SECRET: ${{ secrets.STREAM_SDK_TEST_ACCOUNT_OTP_SECRET }}

jobs:
test:
timeout-minutes: 15
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
with:
fetch-depth: 1

- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: 18.x
cache: 'yarn'
cache-dependency-path: 'yarn.lock'

- name: Install dependencies
run: yarn install --immutable

- name: Build packages
env:
NODE_ENV: production
run: yarn build:react:deps

- name: Cache Playwright browsers
uses: actions/cache@v3
id: playwright-cache
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}${{ runner.arch }}-playwright-browsers

- name: Install Playwright browsers if not cached
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: npx playwright install chromium

- name: Install Playwright system dependencies (always)
run: npx playwright install-deps

- name: Run Playwright tests
working-directory: sample-apps/react/egress-composite
run: yarn buddy auth && yarn test:e2e
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ export const ParticipantView = forwardRef<HTMLDivElement, ParticipantViewProps>(

return (
<div
data-testid="participant-view"
ref={(element) => {
applyElementToRef(ref, element);
setTrackedElement(element);
Expand Down
3 changes: 3 additions & 0 deletions sample-apps/react/egress-composite/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ dist-ssr
*.sln
*.sw?
.vercel
/test-results/
/playwright-report/
/playwright/.cache/
8 changes: 7 additions & 1 deletion sample-apps/react/egress-composite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
"start": "vite",
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
"preview": "vite preview",
"test:e2e": "playwright test",
"buddy": "stream-video-buddy"
},
"dependencies": {
"@stream-io/video-react-sdk": "workspace:^",
Expand All @@ -17,9 +19,13 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
"@playwright/test": "^1.37.1",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@vitejs/plugin-react": "^4.0.0",
"axios": "^1.5.0",
"nanoid": "^4.0.2",
"stream-video-buddy": "https://github.com/GetStream/stream-video-buddy#1.6.10",
"typescript": "^4.9.5",
"vercel": "^32.1.0",
"vite": "^4.4.9"
Expand Down
42 changes: 42 additions & 0 deletions sample-apps/react/egress-composite/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { defineConfig } from '@playwright/test';

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests',
fullyParallel: true,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'list',
// custom path to omit architecture/system from file name (darwin-amd64/darwin-arm64/linux-amd64...)
snapshotPathTemplate: './tests/__screenshots__/{testFilePath}/{arg}{ext}',
expect: {
toHaveScreenshot: {
// to account for CI headless
maxDiffPixelRatio: 0.05,
},
},
use: {
headless: !!process.env.CI,
trace: 'on-first-retry',
viewport: { width: 1920, height: 1080 },
baseURL: 'http://localhost:5173',
// TODO: find out why custom data-test-id does not work
// testIdAttribute: 'data-testid',
},
webServer: [
{
timeout: 10000,
command: 'yarn buddy server --port 4567',
reuseExistingServer: false,
port: 4567,
},
{
timeout: 10000,
command: 'yarn dev',
reuseExistingServer: false,
port: 5173,
},
],
});
81 changes: 12 additions & 69 deletions sample-apps/react/egress-composite/src/CompositeApp.tsx
Original file line number Diff line number Diff line change
@@ -1,95 +1,38 @@
import { PropsWithChildren, useEffect, useState } from 'react';
import { PropsWithChildren } from 'react';
import {
Call,
CallingState,
StreamCallProvider,
StreamClientOptions,
StreamTheme,
StreamVideo,
StreamVideoClient,
} from '@stream-io/video-react-sdk';

import { useConfigurationContext } from './ConfigurationContext';
import { EgressReadyNotificationProvider, useExternalCSS } from './hooks';
import {
EgressReadyNotificationProvider,
useExternalCSS,
useInitializeClientAndCall,
} from './hooks';
import { UIDispatcher, LogoAndTitleOverlay } from './components';

import './CompositeApp.scss';

export const CompositeApp = () => {
const {
base_url: baseURL,
api_key: apiKey,
user_id: userId,
call_type: callType,
call_id: callId,
token,
} = useConfigurationContext();

const options: StreamClientOptions = {};
if (baseURL) {
options.baseURL = baseURL;
}
const [client] = useState<StreamVideoClient>(
() => new StreamVideoClient(apiKey, options),
);

useEffect(() => {
client.connectUser(
{
id: userId,
},
token,
);

return () => {
client.disconnectUser();
};
}, [client, token, userId]);

const [activeCall, setActiveCall] = useState<Call>();
useEffect(() => {
if (!client) return;
let joinInterrupted = false;
const call = client.call(callType, callId);
const currentCall = call.join().then(() => {
if (!joinInterrupted) {
setActiveCall(call);
}
return call;
});
return () => {
joinInterrupted = true;
currentCall.then((theCall) => {
if (theCall && theCall.state.callingState !== CallingState.LEFT) {
theCall.leave();
}
setActiveCall(undefined);
});
};
}, [client, callType, callId]);

if (!client) {
return <h2>Connecting...</h2>;
}
const { client, call } = useInitializeClientAndCall();

return (
<StreamVideo client={client}>
<StreamThemeWrapper>
<EgressReadyNotificationProvider>
{activeCall && (
<StreamCallProvider call={activeCall}>
<UIDispatcher />
<LogoAndTitleOverlay />
</StreamCallProvider>
)}
<StreamCallProvider call={call}>
<UIDispatcher />
<LogoAndTitleOverlay />
</StreamCallProvider>
</EgressReadyNotificationProvider>
{/* <StyleComponent /> */}
</StreamThemeWrapper>
</StreamVideo>
);
};

const StreamThemeWrapper = ({ children }: PropsWithChildren) => {
export const StreamThemeWrapper = ({ children }: PropsWithChildren) => {
// TODO: background style
useExternalCSS();

Expand Down
32 changes: 32 additions & 0 deletions sample-apps/react/egress-composite/src/ConfigurationContext.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { createContext, useContext } from 'react';
import { decode } from 'js-base64';
import { StreamVideoParticipant } from '@stream-io/video-react-sdk';

const DEFAULT_USER_ID = 'egress';
const DEFAULT_CALL_TYPE = 'default';

export type ConfigurationValue = {
base_url?: string;
Expand All @@ -15,6 +19,10 @@ export type ConfigurationValue = {
layout?: 'grid' | 'single_participant' | 'spotlight' | 'mobile';
screenshare_layout?: 'single_participant' | 'spotlight';

test_environment?: {
participants?: Partial<StreamVideoParticipant>[];
};

options: {
'video.background_color'?: string;
'video.scale_mode'?: 'fill' | 'fit';
Expand Down Expand Up @@ -72,3 +80,27 @@ export const extractPayloadFromToken = (token: string) => {
};

export const useConfigurationContext = () => useContext(ConfigurationContext);

export const applyConfigurationDefaults = (
configuration: ConfigurationValue,
) => {
const {
// apply defaults
api_key = import.meta.env.VITE_STREAM_API_KEY as string,
token = import.meta.env.VITE_STREAM_USER_TOKEN as string,
user_id = (extractPayloadFromToken(token as string)['user_id'] ??
DEFAULT_USER_ID) as string,
call_type = DEFAULT_CALL_TYPE,
options = {},
...rest
} = configuration;

return {
api_key,
token,
user_id,
call_type,
options,
...rest,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ export const LogoAndTitleOverlay = () => {
>
{/* {text?.length && (
<div
data-test-id="title"
data-testid="title"
style={{ ...DEFAULT_TITLE_STYLE, ...titleStyle }}
>
{text}
</div>
)} */}
{image_url && (
<img
data-test-id="logo"
data-testid="logo"
src={image_url}
// style={{ ...DEFAULT_LOGO_STYLE, ...logoStyle }}
alt="logo"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export const UIDispatcher = () => {
const hasScreenShare = useHasOngoingScreenShare();

const DefaultView = layoutMap[layout]?.[0] ?? Spotlight;

const ScreenShareView = layoutMap[screenshare_layout]?.[1] ?? Spotlight;

return hasScreenShare ? <ScreenShareView /> : <DefaultView />;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import {
} from '@stream-io/video-react-sdk';
import { useSpotlightParticipant } from './useSpotlightParticipant';
import { useEgressReadyWhenAnyParticipantMounts } from '../egressReady';
import './DominantSpeaker.scss';
import { AudioTracks } from './AudioTracks';

import './DominantSpeaker.scss';

export const DominantSpeaker = () => {
const activeCall = useCall();
const speakerInSpotlight = useSpotlightParticipant();
Expand All @@ -24,7 +25,10 @@ export const DominantSpeaker = () => {
if (!activeCall) return <h2>No active call</h2>;
return (
<>
<div className="dominant-speaker__container">
<div
className="dominant-speaker__container"
data-testid="single_participant"
>
{speakerInSpotlight && (
<ParticipantView
participant={speakerInSpotlight}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const PaginatedGrid = () => {
} = useConfigurationContext();

return (
<div className="paginated-grid">
<div className="paginated-grid" data-testid="grid">
<PaginatedGridLayout
ParticipantViewUI={
<DefaultParticipantViewUI
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const Spotlight = () => {
} = useConfigurationContext();

return (
<div className="spotlight">
<div className="spotlight" data-testid="spotlight">
<SpeakerLayout
participantsBarPosition={barPosition}
ParticipantViewUIBar={
Expand Down
1 change: 1 addition & 0 deletions sample-apps/react/egress-composite/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './useExternalCSS';
export * from './useNotifyEgress';
export * from './useInitializeClient';
Loading

0 comments on commit da140cb

Please sign in to comment.