diff --git a/.github/workflows/egress-composite-e2e.yml b/.github/workflows/egress-composite-e2e.yml new file mode 100644 index 0000000000..8ad7c8b71f --- /dev/null +++ b/.github/workflows/egress-composite-e2e.yml @@ -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 diff --git a/packages/react-sdk/src/core/components/ParticipantView/ParticipantView.tsx b/packages/react-sdk/src/core/components/ParticipantView/ParticipantView.tsx index 090b0c8161..604a89eef9 100644 --- a/packages/react-sdk/src/core/components/ParticipantView/ParticipantView.tsx +++ b/packages/react-sdk/src/core/components/ParticipantView/ParticipantView.tsx @@ -147,6 +147,7 @@ export const ParticipantView = forwardRef( return (
{ applyElementToRef(ref, element); setTrackedElement(element); diff --git a/sample-apps/react/egress-composite/.gitignore b/sample-apps/react/egress-composite/.gitignore index fc5ae9f0cc..1b6016cd16 100644 --- a/sample-apps/react/egress-composite/.gitignore +++ b/sample-apps/react/egress-composite/.gitignore @@ -23,3 +23,6 @@ dist-ssr *.sln *.sw? .vercel +/test-results/ +/playwright-report/ +/playwright/.cache/ diff --git a/sample-apps/react/egress-composite/package.json b/sample-apps/react/egress-composite/package.json index 5b08a4aa2b..b4a972e9eb 100644 --- a/sample-apps/react/egress-composite/package.json +++ b/sample-apps/react/egress-composite/package.json @@ -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:^", @@ -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" diff --git a/sample-apps/react/egress-composite/playwright.config.ts b/sample-apps/react/egress-composite/playwright.config.ts new file mode 100644 index 0000000000..84b3b2438f --- /dev/null +++ b/sample-apps/react/egress-composite/playwright.config.ts @@ -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, + }, + ], +}); diff --git a/sample-apps/react/egress-composite/src/CompositeApp.tsx b/sample-apps/react/egress-composite/src/CompositeApp.tsx index c3b1dd7f39..fde2b7cc71 100644 --- a/sample-apps/react/egress-composite/src/CompositeApp.tsx +++ b/sample-apps/react/egress-composite/src/CompositeApp.tsx @@ -1,87 +1,30 @@ -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( - () => new StreamVideoClient(apiKey, options), - ); - - useEffect(() => { - client.connectUser( - { - id: userId, - }, - token, - ); - - return () => { - client.disconnectUser(); - }; - }, [client, token, userId]); - - const [activeCall, setActiveCall] = useState(); - 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

Connecting...

; - } + const { client, call } = useInitializeClientAndCall(); return ( - {activeCall && ( - - - - - )} + + + + {/* */} @@ -89,7 +32,7 @@ export const CompositeApp = () => { ); }; -const StreamThemeWrapper = ({ children }: PropsWithChildren) => { +export const StreamThemeWrapper = ({ children }: PropsWithChildren) => { // TODO: background style useExternalCSS(); diff --git a/sample-apps/react/egress-composite/src/ConfigurationContext.tsx b/sample-apps/react/egress-composite/src/ConfigurationContext.tsx index 5a5c811db4..39d39cf85c 100644 --- a/sample-apps/react/egress-composite/src/ConfigurationContext.tsx +++ b/sample-apps/react/egress-composite/src/ConfigurationContext.tsx @@ -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; @@ -15,6 +19,10 @@ export type ConfigurationValue = { layout?: 'grid' | 'single_participant' | 'spotlight' | 'mobile'; screenshare_layout?: 'single_participant' | 'spotlight'; + test_environment?: { + participants?: Partial[]; + }; + options: { 'video.background_color'?: string; 'video.scale_mode'?: 'fill' | 'fit'; @@ -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, + }; +}; diff --git a/sample-apps/react/egress-composite/src/components/LogoAndTitleOverlay.tsx b/sample-apps/react/egress-composite/src/components/LogoAndTitleOverlay.tsx index 378763fc7f..241f875fbb 100644 --- a/sample-apps/react/egress-composite/src/components/LogoAndTitleOverlay.tsx +++ b/sample-apps/react/egress-composite/src/components/LogoAndTitleOverlay.tsx @@ -17,7 +17,7 @@ export const LogoAndTitleOverlay = () => { > {/* {text?.length && (
{text} @@ -25,7 +25,7 @@ export const LogoAndTitleOverlay = () => { )} */} {image_url && ( logo { const hasScreenShare = useHasOngoingScreenShare(); const DefaultView = layoutMap[layout]?.[0] ?? Spotlight; - const ScreenShareView = layoutMap[screenshare_layout]?.[1] ?? Spotlight; return hasScreenShare ? : ; diff --git a/sample-apps/react/egress-composite/src/components/layouts/DominantSpeaker/DominantSpeaker.tsx b/sample-apps/react/egress-composite/src/components/layouts/DominantSpeaker/DominantSpeaker.tsx index e644087cec..3ac57b216b 100644 --- a/sample-apps/react/egress-composite/src/components/layouts/DominantSpeaker/DominantSpeaker.tsx +++ b/sample-apps/react/egress-composite/src/components/layouts/DominantSpeaker/DominantSpeaker.tsx @@ -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(); @@ -24,7 +25,10 @@ export const DominantSpeaker = () => { if (!activeCall) return

No active call

; return ( <> -
+
{speakerInSpotlight && ( { } = useConfigurationContext(); return ( -
+
{ } = useConfigurationContext(); return ( -
+
{ + const { test_environment: testEnvironment } = useConfigurationContext(); + + useEffect(() => { + if (!enabled) return; + + const { participants = [] } = testEnvironment ?? {}; + // @ts-ignore + client.writeableStateStore.registerCall(call); + call.state.setParticipants(participants as StreamVideoParticipant[]); + console.log({ client, call }); + }, [client, call, testEnvironment, enabled]); +}; + +const useJoinCall = ({ + client, + call, + enabled, +}: { + client: StreamVideoClient; + call: Call; + enabled: boolean; +}) => { + const { token, user_id: userId } = useConfigurationContext(); + + useEffect(() => { + if (!enabled) return; + + client.connectUser( + { + id: userId, + }, + token, + ); + + return () => { + client.disconnectUser(); + }; + }, [client, enabled, token, userId]); + + useEffect(() => { + if (!client || !enabled) return; + + const callJoinPromise = call.join(); + return () => { + callJoinPromise.then(() => { + call.leave(); + }); + }; + }, [call, client, enabled]); +}; + +export const useInitializeClientAndCall = () => { + const { + base_url: baseURL, + api_key: apiKey, + call_type: callType, + call_id: callId, + test_environment: testEnvironment, + } = useConfigurationContext(); + + const client = useMemo(() => { + return new StreamVideoClient(apiKey, { + baseURL, + }); + }, [apiKey, baseURL]); + + const call = useMemo(() => { + return client.call(callType, callId); + }, [callId, callType, client]); + + // mock state of client and call if "test_environment" exists + useVideoStateMocks({ client, call, enabled: !!testEnvironment }); + // join call and proceed normally + useJoinCall({ client, call, enabled: !testEnvironment }); + + return { client, call }; +}; diff --git a/sample-apps/react/egress-composite/src/main.tsx b/sample-apps/react/egress-composite/src/main.tsx index e121d40526..0f123a29b5 100644 --- a/sample-apps/react/egress-composite/src/main.tsx +++ b/sample-apps/react/egress-composite/src/main.tsx @@ -2,37 +2,15 @@ import { createRoot } from 'react-dom/client'; import { ConfigurationContext, ConfigurationValue, - extractPayloadFromToken, + applyConfigurationDefaults, } from './ConfigurationContext'; import { CompositeApp } from './CompositeApp'; import '@stream-io/video-react-sdk/dist/css/styles.css'; -const DEFAULT_USER_ID = 'egress'; -const DEFAULT_CALL_TYPE = 'default'; - // @ts-expect-error TODO: this is a global function, we need to declare it window.setupLayout = (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; - - const newConfiguration = { - api_key, - token, - user_id, - call_type, - options, - ...rest, - }; - + const newConfiguration = applyConfigurationDefaults(configuration); console.log('Mounting with config:', { configuration: newConfiguration }); createRoot(document.getElementById('root') as HTMLElement).render( diff --git a/sample-apps/react/egress-composite/tests/__screenshots__/layouts.spec.ts/Layouts-Should-render-default-screenshare-layout-1.png b/sample-apps/react/egress-composite/tests/__screenshots__/layouts.spec.ts/Layouts-Should-render-default-screenshare-layout-1.png new file mode 100644 index 0000000000..bc444c86a9 Binary files /dev/null and b/sample-apps/react/egress-composite/tests/__screenshots__/layouts.spec.ts/Layouts-Should-render-default-screenshare-layout-1.png differ diff --git a/sample-apps/react/egress-composite/tests/__screenshots__/layouts.spec.ts/Layouts-Should-render-layout---default-1.png b/sample-apps/react/egress-composite/tests/__screenshots__/layouts.spec.ts/Layouts-Should-render-layout---default-1.png new file mode 100644 index 0000000000..6587bb6f21 Binary files /dev/null and b/sample-apps/react/egress-composite/tests/__screenshots__/layouts.spec.ts/Layouts-Should-render-layout---default-1.png differ diff --git a/sample-apps/react/egress-composite/tests/__screenshots__/layouts.spec.ts/Layouts-Should-render-layout---grid-1.png b/sample-apps/react/egress-composite/tests/__screenshots__/layouts.spec.ts/Layouts-Should-render-layout---grid-1.png new file mode 100644 index 0000000000..757166959f Binary files /dev/null and b/sample-apps/react/egress-composite/tests/__screenshots__/layouts.spec.ts/Layouts-Should-render-layout---grid-1.png differ diff --git a/sample-apps/react/egress-composite/tests/__screenshots__/layouts.spec.ts/Layouts-Should-render-layout---single-participant-1.png b/sample-apps/react/egress-composite/tests/__screenshots__/layouts.spec.ts/Layouts-Should-render-layout---single-participant-1.png new file mode 100644 index 0000000000..b967a7fa17 Binary files /dev/null and b/sample-apps/react/egress-composite/tests/__screenshots__/layouts.spec.ts/Layouts-Should-render-layout---single-participant-1.png differ diff --git a/sample-apps/react/egress-composite/tests/__screenshots__/layouts.spec.ts/Layouts-Should-render-layout---spotlight-1.png b/sample-apps/react/egress-composite/tests/__screenshots__/layouts.spec.ts/Layouts-Should-render-layout---spotlight-1.png new file mode 100644 index 0000000000..6587bb6f21 Binary files /dev/null and b/sample-apps/react/egress-composite/tests/__screenshots__/layouts.spec.ts/Layouts-Should-render-layout---spotlight-1.png differ diff --git a/sample-apps/react/egress-composite/tests/baseTests.ts b/sample-apps/react/egress-composite/tests/baseTests.ts new file mode 100644 index 0000000000..e5207db2ca --- /dev/null +++ b/sample-apps/react/egress-composite/tests/baseTests.ts @@ -0,0 +1,43 @@ +import { test as base } from '@playwright/test'; +import { customAlphabet } from 'nanoid'; +import axios from 'axios'; + +const nanoid = customAlphabet('1234567890abcdefghijklmnop', 10); + +export const testWithCallId = base.extend<{ callId: string }>({ + callId: async ({ page }, use) => { + const callId = nanoid(); + + await page.goto('/', { waitUntil: 'domcontentloaded' }); + + // run tests + await use(callId); + }, +}); + +// TODO: find better name +export const testWithBuddy = base.extend<{ callId: string }>({ + callId: async ({ page }, use) => { + const callId = nanoid(); + + // TODO: have proper abstractions with typing like StreamVideoBuddy.join(...) -> id and StreamVideoBuddy.teardown(id) + await axios.post('http://localhost:4567/stream-video-buddy?async=true', { + duration: 60, + 'call-id': callId, + 'user-count': 4, + }); + + // TODO: have proper ?asyncJoin=true + // which waits only for successfull call creation + // but asyncs user joins + await new Promise((resolve) => setTimeout(resolve, 2000)); + + await page.goto('/', { waitUntil: 'domcontentloaded' }); + + // run tests + await use(callId); + + // teardown + // TODO: await StreamVideoBuddy.teardown()... + }, +}); diff --git a/sample-apps/react/egress-composite/tests/layouts.spec.ts b/sample-apps/react/egress-composite/tests/layouts.spec.ts new file mode 100644 index 0000000000..f5fd2477cf --- /dev/null +++ b/sample-apps/react/egress-composite/tests/layouts.spec.ts @@ -0,0 +1,66 @@ +import { expect } from '@playwright/test'; + +import { testWithCallId as test } from './baseTests'; +import { + generateScriptTagContent, + participants, + participantsWithScreenShare, +} from './mocks'; + +const DEFAULT_LAYOUT = 'spotlight'; + +test.describe('Layouts', () => { + ( + [ + { name: undefined, participantCountPerWindow: 5 }, // default + { name: 'grid', participantCountPerWindow: 5 }, + { name: 'single_participant', participantCountPerWindow: 1 }, + { name: 'spotlight', participantCountPerWindow: 5 }, + ] as const + ).forEach((layout) => { + test(`Should render layout - ${layout.name ?? 'default'}`, async ({ + page, + callId, + }) => { + await page.addScriptTag({ + content: generateScriptTagContent({ + call_id: callId, + layout: layout.name, + test_environment: { + participants, + }, + }), + }); + + await expect(page.getByTestId('participant-view')).toHaveCount( + layout.participantCountPerWindow, + ); + + await expect( + page.getByTestId(layout.name ?? DEFAULT_LAYOUT), + ).toBeVisible(); + + await expect(page).toHaveScreenshot({ + mask: [page.getByTestId('participant-view')], + maskColor: 'lime', + }); + }); + }); + + test('Should render default screenshare layout', async ({ page, callId }) => { + await page.addScriptTag({ + content: generateScriptTagContent({ + call_id: callId, + test_environment: { + participants: participantsWithScreenShare, + }, + }), + }); + + await expect(page.getByTestId('participant-view')).toHaveCount(6); + + await expect(page.getByTestId(DEFAULT_LAYOUT)).toBeVisible(); + + await expect(page).toHaveScreenshot(); + }); +}); diff --git a/sample-apps/react/egress-composite/tests/mocks.ts b/sample-apps/react/egress-composite/tests/mocks.ts new file mode 100644 index 0000000000..bb495d58ab --- /dev/null +++ b/sample-apps/react/egress-composite/tests/mocks.ts @@ -0,0 +1,27 @@ +import { StreamVideoParticipant } from '@stream-io/video-react-sdk'; +import { ConfigurationValue } from '../src/ConfigurationContext'; + +const users = ['john', 'jane', 'mark', 'martin', 'anne']; + +export const generateScriptTagContent = (data: Partial) => { + return `window.setupLayout(${JSON.stringify(data)});`; +}; + +export const participants = users.map>( + (user, index) => ({ + userId: user, + sessionId: `${user}_${index}`, + publishedTracks: [], + isSpeaking: false, + }), +); + +export const participantsWithScreenShare = users.map< + Partial +>((user, index) => ({ + userId: user, + sessionId: `${user}_${index}`, + // FIXME: figure out why SfuModels cannot be imported + publishedTracks: !index ? [] : [3], + isSpeaking: false, +})); diff --git a/yarn.lock b/yarn.lock index b08e68decf..04de8d100c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4533,6 +4533,22 @@ __metadata: languageName: node linkType: hard +"@playwright/test@npm:^1.37.1": + version: 1.37.1 + resolution: "@playwright/test@npm:1.37.1" + dependencies: + "@types/node": "*" + fsevents: 2.3.2 + playwright-core: 1.37.1 + dependenciesMeta: + fsevents: + optional: true + bin: + playwright: cli.js + checksum: b7038f29000289103c08b215eff7aabdda70cdc1375fa7dad0e81651be71086a1e2fc0e0e29dc70348037c366cf0cc69f762373fda34ba1a74aa1658741d9195 + languageName: node + linkType: hard + "@pmmmwh/react-refresh-webpack-plugin@npm:^0.5.3": version: 0.5.10 resolution: "@pmmmwh/react-refresh-webpack-plugin@npm:0.5.10" @@ -6850,14 +6866,18 @@ __metadata: version: 0.0.0-use.local resolution: "@stream-io/egress-composite@workspace:sample-apps/react/egress-composite" dependencies: + "@playwright/test": ^1.37.1 "@stream-io/video-react-sdk": "workspace:^" "@types/react": ^18.0.28 "@types/react-dom": ^18.0.11 "@vitejs/plugin-react": ^4.0.0 + axios: ^1.5.0 clsx: ^1.2.1 js-base64: ^3.7.5 + nanoid: ^4.0.2 react: ^18.2.0 react-dom: ^18.2.0 + 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 @@ -7556,6 +7576,15 @@ __metadata: languageName: node linkType: hard +"@types/debug@npm:^4.1.0": + version: 4.1.8 + resolution: "@types/debug@npm:4.1.8" + dependencies: + "@types/ms": "*" + checksum: a9a9bb40a199e9724aa944e139a7659173a9b274798ea7efbc277cb084bc37d32fc4c00877c3496fac4fed70a23243d284adb75c00b5fdabb38a22154d18e5df + languageName: node + linkType: hard + "@types/eslint-scope@npm:^3.7.3": version: 3.7.4 resolution: "@types/eslint-scope@npm:3.7.4" @@ -10018,6 +10047,17 @@ __metadata: languageName: node linkType: hard +"axios@npm:^1.5.0": + version: 1.5.0 + resolution: "axios@npm:1.5.0" + dependencies: + follow-redirects: ^1.15.0 + form-data: ^4.0.0 + proxy-from-env: ^1.1.0 + checksum: e7405a5dbbea97760d0e6cd58fecba311b0401ddb4a8efbc4108f5537da9b3f278bde566deb777935a960beec4fa18e7b8353881f2f465e4f2c0e949fead35be + languageName: node + linkType: hard + "axobject-query@npm:^3.1.1": version: 3.1.1 resolution: "axobject-query@npm:3.1.1" @@ -11484,6 +11524,19 @@ __metadata: languageName: node linkType: hard +"clone-deep@npm:^0.2.4": + version: 0.2.4 + resolution: "clone-deep@npm:0.2.4" + dependencies: + for-own: ^0.1.3 + is-plain-object: ^2.0.1 + kind-of: ^3.0.2 + lazy-cache: ^1.0.3 + shallow-clone: ^0.1.2 + checksum: bcf9752052130c270c47d3e1c357497354b91d682f507e0079bec5950975b3293b619d9e100d70874606d716f2376e84956b045759a09af703e1038ecad6c438 + languageName: node + linkType: hard + "clone-deep@npm:^2.0.1": version: 2.0.2 resolution: "clone-deep@npm:2.0.2" @@ -11713,7 +11766,7 @@ __metadata: languageName: node linkType: hard -"commander@npm:^9.4.1": +"commander@npm:^9.4.1, commander@npm:^9.5.0": version: 9.5.0 resolution: "commander@npm:9.5.0" checksum: c7a3e27aa59e913b54a1bafd366b88650bc41d6651f0cbe258d4ff09d43d6a7394232a4dadd0bf518b3e696fdf595db1028a0d82c785b88bd61f8a440cecfade @@ -15628,6 +15681,15 @@ __metadata: languageName: node linkType: hard +"for-own@npm:^0.1.3": + version: 0.1.5 + resolution: "for-own@npm:0.1.5" + dependencies: + for-in: ^1.0.1 + checksum: 07eb0a2e98eb55ce13b56dd11ef4fb5e619ba7380aaec388b9eec1946153d74fa734ce409e8434020557e9489a50c34bc004d55754f5863bf7d77b441d8dee8c + languageName: node + linkType: hard + "for-own@npm:^1.0.0": version: 1.0.0 resolution: "for-own@npm:1.0.0" @@ -15800,7 +15862,7 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:10.1.0, fs-extra@npm:^10.1.0": +"fs-extra@npm:10.1.0, fs-extra@npm:^10.0.0, fs-extra@npm:^10.1.0": version: 10.1.0 resolution: "fs-extra@npm:10.1.0" dependencies: @@ -15903,6 +15965,16 @@ __metadata: languageName: node linkType: hard +"fsevents@npm:2.3.2, fsevents@npm:^2.1.2, fsevents@npm:^2.3.2, fsevents@npm:~2.3.2": + version: 2.3.2 + resolution: "fsevents@npm:2.3.2" + dependencies: + node-gyp: latest + checksum: 97ade64e75091afee5265e6956cb72ba34db7819b4c3e94c431d4be2b19b8bb7a2d4116da417950c3425f17c8fe693d25e20212cac583ac1521ad066b77ae31f + conditions: os=darwin + languageName: node + linkType: hard + "fsevents@npm:^1.2.7": version: 1.2.13 resolution: "fsevents@npm:1.2.13" @@ -15914,12 +15986,11 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:^2.1.2, fsevents@npm:^2.3.2, fsevents@npm:~2.3.2": +"fsevents@patch:fsevents@2.3.2#~builtin, fsevents@patch:fsevents@^2.1.2#~builtin, fsevents@patch:fsevents@^2.3.2#~builtin, fsevents@patch:fsevents@~2.3.2#~builtin": version: 2.3.2 - resolution: "fsevents@npm:2.3.2" + resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=df0bf1" dependencies: node-gyp: latest - checksum: 97ade64e75091afee5265e6956cb72ba34db7819b4c3e94c431d4be2b19b8bb7a2d4116da417950c3425f17c8fe693d25e20212cac583ac1521ad066b77ae31f conditions: os=darwin languageName: node linkType: hard @@ -15934,15 +16005,6 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@^2.1.2#~builtin, fsevents@patch:fsevents@^2.3.2#~builtin, fsevents@patch:fsevents@~2.3.2#~builtin": - version: 2.3.2 - resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=df0bf1" - dependencies: - node-gyp: latest - conditions: os=darwin - languageName: node - linkType: hard - "function-bind@npm:^1.1.1": version: 1.1.1 resolution: "function-bind@npm:1.1.1" @@ -17545,7 +17607,7 @@ __metadata: languageName: node linkType: hard -"is-buffer@npm:^1.1.5, is-buffer@npm:~1.1.1, is-buffer@npm:~1.1.6": +"is-buffer@npm:^1.0.2, is-buffer@npm:^1.1.5, is-buffer@npm:~1.1.1, is-buffer@npm:~1.1.6": version: 1.1.6 resolution: "is-buffer@npm:1.1.6" checksum: 4a186d995d8bbf9153b4bd9ff9fd04ae75068fe695d29025d25e592d9488911eeece84eefbd8fa41b8ddcc0711058a71d4c466dcf6f1f6e1d83830052d8ca707 @@ -17905,7 +17967,7 @@ __metadata: languageName: node linkType: hard -"is-plain-object@npm:^2.0.3, is-plain-object@npm:^2.0.4": +"is-plain-object@npm:^2.0.1, is-plain-object@npm:^2.0.3, is-plain-object@npm:^2.0.4": version: 2.0.4 resolution: "is-plain-object@npm:2.0.4" dependencies: @@ -19194,6 +19256,13 @@ __metadata: languageName: node linkType: hard +"jssha@npm:^3.3.0": + version: 3.3.1 + resolution: "jssha@npm:3.3.1" + checksum: 4f2b636305710d5275d5e48a323ceaed80b6400fd545321fccdb4e904392a7b808fa79cdf355073766a290d731756eb5bd5ceb2341cc53e57b686c7bb3a18488 + languageName: node + linkType: hard + "jsx-ast-utils@npm:^2.4.1 || ^3.0.0, jsx-ast-utils@npm:^3.3.3": version: 3.3.3 resolution: "jsx-ast-utils@npm:3.3.3" @@ -19246,6 +19315,15 @@ __metadata: languageName: node linkType: hard +"kind-of@npm:^2.0.1": + version: 2.0.1 + resolution: "kind-of@npm:2.0.1" + dependencies: + is-buffer: ^1.0.2 + checksum: 043df2943e113bca612d26224947395e9673bb3808d94aed30e47fbf0bafd618e2a29ff0ca2d5498f64332c320fff07f0aa9d6edfc20906a93c1b8792f11759c + languageName: node + linkType: hard + "kind-of@npm:^3.0.2, kind-of@npm:^3.0.3, kind-of@npm:^3.2.0": version: 3.2.2 resolution: "kind-of@npm:3.2.2" @@ -19324,6 +19402,20 @@ __metadata: languageName: node linkType: hard +"lazy-cache@npm:^0.2.3": + version: 0.2.7 + resolution: "lazy-cache@npm:0.2.7" + checksum: b4538aff20db586c354f31de3ed59ea2c8d5dc4f01141bf49f07601e7ca0d7ed43a3f49362ade49b1e18ab1f3d121df0f2c9ea9b599b44dd54fb0c0db253c8b9 + languageName: node + linkType: hard + +"lazy-cache@npm:^1.0.3": + version: 1.0.4 + resolution: "lazy-cache@npm:1.0.4" + checksum: e6650c22e5de1cc3f4a0c25d2b35fe9cd400473c1b3562be9fceadf8f368d708b54d24f5aa51b321b090da65b36426823a8f706b8dbdd68270db0daba812c5d3 + languageName: node + linkType: hard + "lazy-universal-dotenv@npm:^3.0.1": version: 3.0.1 resolution: "lazy-universal-dotenv@npm:3.0.1" @@ -20372,6 +20464,17 @@ __metadata: languageName: node linkType: hard +"merge-deep@npm:^3.0.1": + version: 3.0.3 + resolution: "merge-deep@npm:3.0.3" + dependencies: + arr-union: ^3.1.0 + clone-deep: ^0.2.4 + kind-of: ^3.0.2 + checksum: d2eb367b8300327c66a3e1e01eb06251f51b440bf5bfa5f0f8065ae95bf3af620d21fcd0ab2eb50e74f5119aac40ffd26c85e3bf82f79082e8757675f5885d3d + languageName: node + linkType: hard + "merge-descriptors@npm:1.0.1": version: 1.0.1 resolution: "merge-descriptors@npm:1.0.1" @@ -23589,6 +23692,43 @@ __metadata: languageName: node linkType: hard +"playwright-core@npm:1.37.1": + version: 1.37.1 + resolution: "playwright-core@npm:1.37.1" + bin: + playwright-core: cli.js + checksum: 69f818da2230057584140d5b3af7778a4f4a822b5b18d133abfc5d259128becb943c343a2ddf6b0635277a69f28983e83e2bc3fce23595ececb1e410475b6368 + languageName: node + linkType: hard + +"playwright-extra@npm:^4.3.6": + version: 4.3.6 + resolution: "playwright-extra@npm:4.3.6" + dependencies: + debug: ^4.3.4 + peerDependencies: + playwright: "*" + playwright-core: "*" + peerDependenciesMeta: + playwright: + optional: true + playwright-core: + optional: true + checksum: 2ecaef65036895a9c6aa8d97be7d01677690a103bf39b514b8a2c153cf29932b4b205040a6e132f145868323891dcfbb32c7b2d97f9ac64a9f25e2e83a2f706e + languageName: node + linkType: hard + +"playwright@npm:^1.35.0": + version: 1.37.1 + resolution: "playwright@npm:1.37.1" + dependencies: + playwright-core: 1.37.1 + bin: + playwright: cli.js + checksum: 99406ef3e15b83a659cb23ef1d92d9935789aad430580d1e0371087dfdf266891483c6f97cfa06bf5f49f081eacd44245d05d20714f98531edef4cc317044d6b + languageName: node + linkType: hard + "plist@npm:^3.0.5": version: 3.1.0 resolution: "plist@npm:3.1.0" @@ -24171,6 +24311,84 @@ __metadata: languageName: node linkType: hard +"puppeteer-extra-plugin-stealth@npm:^2.11.2": + version: 2.11.2 + resolution: "puppeteer-extra-plugin-stealth@npm:2.11.2" + dependencies: + debug: ^4.1.1 + puppeteer-extra-plugin: ^3.2.3 + puppeteer-extra-plugin-user-preferences: ^2.4.1 + peerDependencies: + playwright-extra: "*" + puppeteer-extra: "*" + peerDependenciesMeta: + playwright-extra: + optional: true + puppeteer-extra: + optional: true + checksum: 13ab2b906c1cd1a75bb346074e6ed75dd33da426e9b7d2e111dee6d497ae9ff42f44c2e5b0d48a04b545e862f9d27d8ecf61fa1863201afc8a9410121ed65a4b + languageName: node + linkType: hard + +"puppeteer-extra-plugin-user-data-dir@npm:^2.4.1": + version: 2.4.1 + resolution: "puppeteer-extra-plugin-user-data-dir@npm:2.4.1" + dependencies: + debug: ^4.1.1 + fs-extra: ^10.0.0 + puppeteer-extra-plugin: ^3.2.3 + rimraf: ^3.0.2 + peerDependencies: + playwright-extra: "*" + puppeteer-extra: "*" + peerDependenciesMeta: + playwright-extra: + optional: true + puppeteer-extra: + optional: true + checksum: f360b3bb22eeb899b23b6e2a38412ddfeda4b2333e08f77452732e16f094f33725272ee81150428e2c20b39d4d88edc87584d8344bd761c9ef8ae5d681968399 + languageName: node + linkType: hard + +"puppeteer-extra-plugin-user-preferences@npm:^2.4.1": + version: 2.4.1 + resolution: "puppeteer-extra-plugin-user-preferences@npm:2.4.1" + dependencies: + debug: ^4.1.1 + deepmerge: ^4.2.2 + puppeteer-extra-plugin: ^3.2.3 + puppeteer-extra-plugin-user-data-dir: ^2.4.1 + peerDependencies: + playwright-extra: "*" + puppeteer-extra: "*" + peerDependenciesMeta: + playwright-extra: + optional: true + puppeteer-extra: + optional: true + checksum: e0e50145d1d32626a8bb75afeb0dc238d63c05a8d264e12bb249b06aa34f7c116f6ec531335e964ea715837759dacd505d2fa8f6c9d478e983303eb2cb701ec9 + languageName: node + linkType: hard + +"puppeteer-extra-plugin@npm:^3.2.3": + version: 3.2.3 + resolution: "puppeteer-extra-plugin@npm:3.2.3" + dependencies: + "@types/debug": ^4.1.0 + debug: ^4.1.1 + merge-deep: ^3.0.1 + peerDependencies: + playwright-extra: "*" + puppeteer-extra: "*" + peerDependenciesMeta: + playwright-extra: + optional: true + puppeteer-extra: + optional: true + checksum: 18c2780a151e023ed145cd7768a6cf2dba1c124bce920de5f7815dcd872550288554161b1474037e8819969b1adfad41bdc09a9aadfd155338abc6d22699b7ed + languageName: node + linkType: hard + "pure-rand@npm:^6.0.0": version: 6.0.1 resolution: "pure-rand@npm:6.0.1" @@ -26573,6 +26791,18 @@ __metadata: languageName: node linkType: hard +"shallow-clone@npm:^0.1.2": + version: 0.1.2 + resolution: "shallow-clone@npm:0.1.2" + dependencies: + is-extendable: ^0.1.1 + kind-of: ^2.0.1 + lazy-cache: ^0.2.3 + mixin-object: ^2.0.1 + checksum: cc4c85c6e42186fec33a81a85622c48dbcfdf280f3a7bd0800b4de57df8e365a8760aa2e31dd79df365b317dddb2fd0bbd92be0aab14dbd2de6a65992eab2177 + languageName: node + linkType: hard + "shallow-clone@npm:^1.0.0": version: 1.0.0 resolution: "shallow-clone@npm:1.0.0" @@ -27319,6 +27549,22 @@ __metadata: languageName: node linkType: hard +"stream-video-buddy@https://github.com/GetStream/stream-video-buddy#1.6.10": + version: 1.6.10 + resolution: "stream-video-buddy@https://github.com/GetStream/stream-video-buddy.git#commit=b73181a7486f259ff270b1c172ecd715c9324806" + dependencies: + commander: ^9.5.0 + express: ^4.18.2 + jssha: ^3.3.0 + playwright: ^1.35.0 + playwright-extra: ^4.3.6 + puppeteer-extra-plugin-stealth: ^2.11.2 + bin: + stream-video-buddy: ./lib/index.js + checksum: 210f0e3af0e04da0cca1e8f99996abb8defa9afd889209d285f6a9e48c07d86ec3d309be6a54e5094a766adb84473f0c07786e557656080a904e03b6aa4e1a4e + languageName: node + linkType: hard + "streamsearch@npm:^1.1.0": version: 1.1.0 resolution: "streamsearch@npm:1.1.0"