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

test(egress-composite): screenshot/snapshot Playwright setup #1023

Merged
merged 14 commits into from
Sep 20, 2023
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
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
Loading