Skip to content
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
1 change: 1 addition & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
{
"files": [
"playwright.config.ts",
"vitest.config.ts",
"**/tests/**",
"**/tests/core/**",
"samples/**/tests/**"
Expand Down
9,409 changes: 6,532 additions & 2,877 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@
},
"devDependencies": {
"@playwright/test": "^1.51.1",
"@testing-library/jest-dom": "^6.9.1",
"@testing-library/react": "^14.3.1",
"@types/node": "^20.4.4",
"@types/react": "^18.2.15",
"@types/react-dom": "^18.2.7",
"@types/sha.js": "^2.4.4",
"@types/xml2js": "^0.4.14",
"@typescript-eslint/eslint-plugin": "^6.2.0",
"@typescript-eslint/parser": "^6.2.0",
"@vitejs/plugin-react": "^4.7.0",
"@vitest/coverage-v8": "^3.2.6",
"axios": "^1.8.4",
"dotenv": "^16.4.7",
"eslint": "^8.45.0",
Expand All @@ -32,11 +36,14 @@
"eslint-plugin-react": "^7.33.0",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-watch": "^8.0.0",
"jsdom": "^25.0.1",
"lint-staged": "11.2.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"sha.js": "^2.4.11",
"typescript": "^5.1.6",
"vite": "^6.4.3",
"vitest": "^3.2.6",
"watch": "^0.13.0",
"xml2js": "^0.6.2"
},
Expand All @@ -48,6 +55,9 @@
"lint:fix": "npm run lint -- --fix",
"lint:watch": "watch 'yarn lint'",
"test": "npx playwright test",
"test:unit": "vitest run",
"test:unit:watch": "vitest",
"test:unit:coverage": "vitest run --coverage",
"test-chromium-ci": "export CI='true' && npx playwright test --project=chromium"
},
"files": [
Expand Down
2 changes: 2 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { server } from './tests/core/parameters';

export default defineConfig({
testDir: process.cwd(),
// Unit tests under tests/unit/ are run by Vitest (npm run test:unit)
testIgnore: ['**/tests/unit/**', '**/node_modules/**'],
workers: CI ? 1 : undefined,
retries: CI ? 1 : 0,
fullyParallel: true,
Expand Down
108 changes: 34 additions & 74 deletions src/data-consumption/factory/hookCreator.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect, useRef } from 'react';
import { useState, useEffect } from 'react';

import { Hooks, HookEvents } from '../../core/enum';
import { CustomSubscriptionArguments } from '../domain/shared/custom-subscription/types';
Expand All @@ -16,120 +16,80 @@ import {
} from '../utils';
import { DataConsumptionHooks } from '../enums';

const updateCustomHookSubscription = (
hookName: Hooks,
handleCustomSubscriptionUpdateEvent: EventListener,
previousQuery?: string,
previousVariables?: object,
currentQuery?: string,
currentVariables?: object,
) => {
window.dispatchEvent(
new CustomEvent<UnsubscribedEventDetails>(HookEvents.PLUGIN_UNSUBSCRIBED_FROM_BBB_CORE, {
detail: {
hook: hookName,
hookArguments: {
query: previousQuery,
variables: previousVariables,
},
},
}),
);
window.dispatchEvent(
new CustomEvent<SubscribedEventDetails>(HookEvents.PLUGIN_SUBSCRIBED_TO_BBB_CORE, {
detail: {
hook: hookName,
hookArguments: {
query: currentQuery,
variables: currentVariables,
},
},
}),
);
window.addEventListener(
HookEvents.BBB_CORE_SENT_NEW_DATA,
handleCustomSubscriptionUpdateEvent,
);
};

export const createDataConsumptionHook = <T>(
hookName: Hooks,
hookArguments?: CustomSubscriptionArguments,
) => {
const [hookData, setHookData] = useState<GraphqlResponseWrapper<T>>({ loading: true });
const prevQueryRef = useRef<string | undefined>(hookArguments?.query);
const prevVariablesRef = useRef<object | undefined>(
hookArguments?.variables,
);
const [queryState, setQueryState] = useState<string | undefined>(hookArguments?.query);
const [variablesState, setVariablesState] = useState<object | undefined>(
hookArguments?.variables,
);

const handleCustomSubscriptionUpdateEvent: EventListener = (
// Only custom subscriptions follow query/variables changes and resubscribe.
// Other hooks keep the arguments they mounted with, so queryState and
// variablesState always mirror what is currently subscribed in BBB core.
if (hookName === DataConsumptionHooks.CUSTOM_SUBSCRIPTION) {
if (hookArguments?.query !== queryState) {
setQueryState(hookArguments?.query);
}
if (
sortedStringify(hookArguments?.variables)
!== sortedStringify(variablesState)
) {
setVariablesState(() => JSON.parse(sortedStringify(hookArguments?.variables)));
}
}

useEffect(() => {
const handleDataUpdateEvent: EventListener = (
(event: HookEventWrapper<GraphqlResponseWrapper<T>>) => {
if (event.detail.hook !== hookName) return;
const detail = event.detail as UpdatedEventDetails<GraphqlResponseWrapper<T>>;
if (hookArguments && event.detail.hook === hookName) {
if (hookArguments) {
const dataConsumptionHookArguments = detail.hookArguments as CustomSubscriptionArguments;
if (queryState && detail.hookArguments && makeCustomHookIdentifierFromArgs(
dataConsumptionHookArguments,
) === makeCustomHookIdentifier(queryState, variablesState)) {
setHookData(detail.data);
}
} else if (!hookArguments && event.detail.hook === hookName) {
} else {
setHookData(detail.data);
}
}) as EventListener;

if (hookArguments?.query !== queryState) {
prevQueryRef.current = queryState;
setQueryState(hookArguments?.query);
}
if (sortedStringify(hookArguments?.variables) !== sortedStringify(variablesState)) {
prevVariablesRef.current = variablesState;
setVariablesState(() => JSON.parse(sortedStringify(hookArguments?.variables)));
}
useEffect(() => {
window.addEventListener(HookEvents.BBB_CORE_SENT_NEW_DATA, handleCustomSubscriptionUpdateEvent);
const subscriptionArguments = hookArguments
? { query: queryState, variables: variablesState }
: undefined;

window.addEventListener(HookEvents.BBB_CORE_SENT_NEW_DATA, handleDataUpdateEvent);
window.dispatchEvent(
new CustomEvent<SubscribedEventDetails>(HookEvents.PLUGIN_SUBSCRIBED_TO_BBB_CORE, {
detail: {
hook: hookName,
hookArguments,
hookArguments: subscriptionArguments,
},
}),
);

// React runs this cleanup before the effect of the next queryState /
// variablesState cycle (and on unmount), so BBB core always receives one
// unsubscribe for exactly the arguments previously subscribed.
return () => {
window.dispatchEvent(
new CustomEvent<UnsubscribedEventDetails>(HookEvents.PLUGIN_UNSUBSCRIBED_FROM_BBB_CORE, {
detail: {
hook: hookName,
hookArguments: {
query: queryState,
variables: variablesState,
},
hookArguments: subscriptionArguments,
},
}),
);
window.removeEventListener(
HookEvents.BBB_CORE_SENT_NEW_DATA,
handleCustomSubscriptionUpdateEvent,
handleDataUpdateEvent,
);
};
}, []);

useEffect(() => {
if (hookName === DataConsumptionHooks.CUSTOM_SUBSCRIPTION
&& (prevQueryRef?.current !== queryState || prevVariablesRef.current !== variablesState)) {
updateCustomHookSubscription(
hookName,
handleCustomSubscriptionUpdateEvent,
prevQueryRef.current,
prevVariablesRef.current,
queryState,
variablesState,
);
}
}, [queryState, variablesState]);

return hookData;
};
4 changes: 4 additions & 0 deletions tests/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@
"moduleResolution": "node",
"noImplicitAny": true,
"noUnusedLocals": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"target": "es5",
},
"exclude": [
"unit/**"
],
}
Loading
Loading