From 82632f83d8b922e639a42af1ca871d04da48bb11 Mon Sep 17 00:00:00 2001 From: Maxim Akimov Date: Thu, 5 Dec 2024 10:38:13 +0200 Subject: [PATCH 1/5] client tests: fix for the captchaImages() random fails --- demos/cypress-shared/cypress/support/commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demos/cypress-shared/cypress/support/commands.ts b/demos/cypress-shared/cypress/support/commands.ts index 9fcfc657b..396cd50d4 100644 --- a/demos/cypress-shared/cypress/support/commands.ts +++ b/demos/cypress-shared/cypress/support/commands.ts @@ -84,7 +84,7 @@ function clickIAmHuman(): Cypress.Chainable { } function captchaImages(): Cypress.Chainable> { - return getWidgetElement("p").then(($p) => { + return getWidgetElement(".prosopo-modalInner p").then(($p) => { const $pWithText = $p.filter((index, el) => { return Cypress.$(el).text().includes("all containing"); }); From 1e270f5d07722fbdcc483e583a9997fa560f457b Mon Sep 17 00:00:00 2001 From: Maxim Akimov Date: Mon, 9 Dec 2024 11:05:05 +0200 Subject: [PATCH 2/5] procaptcha-bundle: renderLogic refactoring --- for-devs.md | 1 + .../src/util/defaultCallbacks.ts | 24 ++-- .../src/util/renderLogic.tsx | 104 ++++------------- .../src/util/renderLogic/captcha/captcha.ts | 25 ++++ .../renderLogic/captcha/captchaRenderer.tsx | 37 ++++++ .../components/frictionlessCaptcha.tsx | 26 +++++ .../captcha/components/imageCaptcha.tsx | 26 +++++ .../captcha/components/powCaptcha.tsx | 26 +++++ .../renderLogic/captcha/componentsList.ts | 30 +++++ .../src/util/renderLogic/webComponent.ts | 49 ++++++++ .../src/util/renderLogic/widgetRenderer.tsx | 108 ++++++++++++++++++ 11 files changed, 361 insertions(+), 95 deletions(-) create mode 100644 for-devs.md create mode 100644 packages/procaptcha-bundle/src/util/renderLogic/captcha/captcha.ts create mode 100644 packages/procaptcha-bundle/src/util/renderLogic/captcha/captchaRenderer.tsx create mode 100644 packages/procaptcha-bundle/src/util/renderLogic/captcha/components/frictionlessCaptcha.tsx create mode 100644 packages/procaptcha-bundle/src/util/renderLogic/captcha/components/imageCaptcha.tsx create mode 100644 packages/procaptcha-bundle/src/util/renderLogic/captcha/components/powCaptcha.tsx create mode 100644 packages/procaptcha-bundle/src/util/renderLogic/captcha/componentsList.ts create mode 100644 packages/procaptcha-bundle/src/util/renderLogic/webComponent.ts create mode 100644 packages/procaptcha-bundle/src/util/renderLogic/widgetRenderer.tsx diff --git a/for-devs.md b/for-devs.md new file mode 100644 index 000000000..7828b0363 --- /dev/null +++ b/for-devs.md @@ -0,0 +1 @@ +fixme \ No newline at end of file diff --git a/packages/procaptcha-bundle/src/util/defaultCallbacks.ts b/packages/procaptcha-bundle/src/util/defaultCallbacks.ts index 01cd8ebed..8bc13474d 100644 --- a/packages/procaptcha-bundle/src/util/defaultCallbacks.ts +++ b/packages/procaptcha-bundle/src/util/defaultCallbacks.ts @@ -29,7 +29,18 @@ export const getWindowCallback = (callbackName: string) => { return fn; }; -export const getDefaultCallbacks = (element: Element) => ({ +export interface Callbacks { + onHuman: (token: ProcaptchaToken) => void; + onChallengeExpired: () => void; + onExpired: () => void; + onError: (error: Error) => void; + onClose: () => void; + onOpen: () => void; + onFailed: () => void; + onReset: () => void; +} + +export const getDefaultCallbacks = (element: Element): Callbacks => ({ onHuman: (token: ProcaptchaToken) => handleOnHuman(element, token), onChallengeExpired: () => { removeProcaptchaResponse(); @@ -60,16 +71,7 @@ export const getDefaultCallbacks = (element: Element) => ({ export function setUserCallbacks( renderOptions: ProcaptchaRenderOptions | undefined, - callbacks: { - onHuman: (token: ProcaptchaToken) => void; - onChallengeExpired: () => void; - onExpired: () => void; - onError: (error: Error) => void; - onClose: () => void; - onOpen: () => void; - onFailed: () => void; - onReset: () => void; - }, + callbacks: Callbacks, element: Element, ) { if (typeof renderOptions?.callback === "function") { diff --git a/packages/procaptcha-bundle/src/util/renderLogic.tsx b/packages/procaptcha-bundle/src/util/renderLogic.tsx index 07feac6ab..4f366e15f 100644 --- a/packages/procaptcha-bundle/src/util/renderLogic.tsx +++ b/packages/procaptcha-bundle/src/util/renderLogic.tsx @@ -1,5 +1,3 @@ -import createCache from "@emotion/cache"; -import { CacheProvider } from "@emotion/react"; // Copyright 2021-2024 Prosopo (UK) Ltd. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -13,94 +11,32 @@ import { CacheProvider } from "@emotion/react"; // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -import { ProcaptchaFrictionless } from "@prosopo/procaptcha-frictionless"; -import { ProcaptchaPow } from "@prosopo/procaptcha-pow"; -import { Procaptcha } from "@prosopo/procaptcha-react"; -import type { - ProcaptchaClientConfigOutput, - ProcaptchaRenderOptions, +import { + FeaturesEnum, + type ProcaptchaClientConfigOutput, + type ProcaptchaRenderOptions, } from "@prosopo/types"; -import { type Root, createRoot } from "react-dom/client"; -import { getDefaultCallbacks, setUserCallbacks } from "./defaultCallbacks.js"; -import { setLanguage } from "./language.js"; -import { setTheme } from "./theme.js"; -import { setValidChallengeLength } from "./timeout.js"; +import { CaptchaRenderer } from "./renderLogic/captcha/captchaRenderer.js"; +import { WebComponent } from "./renderLogic/webComponent.js"; +import { WidgetRenderer } from "./renderLogic/widgetRenderer.js"; -const identifierPrefix = "procaptcha-"; - -function makeShadowRoot( - element: Element, - renderOptions?: ProcaptchaRenderOptions, -): ShadowRoot { - // todo maybe introduce customCSS in renderOptions. - const customCss = ""; - - const wrapperElement = document.createElement("prosopo-procaptcha"); - - const wrapperShadow = wrapperElement.attachShadow({ mode: "open" }); - wrapperShadow.innerHTML += - ''; - wrapperShadow.innerHTML += - "" !== customCss ? `` : ""; - - element.appendChild(wrapperElement); - - return wrapperShadow; -} +const widgetRenderer = new WidgetRenderer( + new WebComponent(), + new CaptchaRenderer(), +); export const renderLogic = ( elements: Element[], config: ProcaptchaClientConfigOutput, renderOptions?: ProcaptchaRenderOptions, ) => { - const roots: Root[] = []; - - for (const element of elements) { - const callbacks = getDefaultCallbacks(element); - const shadowRoot = makeShadowRoot(element, renderOptions); - - setUserCallbacks(renderOptions, callbacks, element); - setTheme(renderOptions, element, config); - setValidChallengeLength(renderOptions, element, config); - setLanguage(renderOptions, element, config); - - const emotionCache = createCache({ - key: "procaptcha", - prepend: true, - container: shadowRoot, - }); - - let root: Root | null = null; - switch (renderOptions?.captchaType) { - case "pow": - console.log("rendering pow"); - root = createRoot(shadowRoot, { identifierPrefix }); - root.render( - - - , - ); - break; - case "image": - console.log("rendering image"); - root = createRoot(shadowRoot, { identifierPrefix }); - root.render( - - - , - ); - break; - default: - console.log("rendering frictionless"); - root = createRoot(shadowRoot, { identifierPrefix }); - root.render( - - - , - ); - break; - } - roots.push(root); - } - return roots; + return widgetRenderer.renderElements( + { + identifierPrefix: "procaptcha-", + defaultCaptchaType: FeaturesEnum.Frictionless, + }, + elements, + config, + renderOptions, + ); }; diff --git a/packages/procaptcha-bundle/src/util/renderLogic/captcha/captcha.ts b/packages/procaptcha-bundle/src/util/renderLogic/captcha/captcha.ts new file mode 100644 index 000000000..7ef95c7ed --- /dev/null +++ b/packages/procaptcha-bundle/src/util/renderLogic/captcha/captcha.ts @@ -0,0 +1,25 @@ +// Copyright 2021-2024 Prosopo (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import type { ProcaptchaClientConfigOutput } from "@prosopo/types"; +import React from "react"; +import type { Callbacks } from "../../defaultCallbacks.js"; + +interface CaptchaProps { + config: ProcaptchaClientConfigOutput; + callbacks: Callbacks; +} + +abstract class CaptchaElement extends React.Component {} + +export { type CaptchaProps, CaptchaElement }; diff --git a/packages/procaptcha-bundle/src/util/renderLogic/captcha/captchaRenderer.tsx b/packages/procaptcha-bundle/src/util/renderLogic/captcha/captchaRenderer.tsx new file mode 100644 index 000000000..9f4fc7d68 --- /dev/null +++ b/packages/procaptcha-bundle/src/util/renderLogic/captcha/captchaRenderer.tsx @@ -0,0 +1,37 @@ +// Copyright 2021-2024 Prosopo (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import type { FeaturesEnum } from "@prosopo/types"; +import type { ReactNode } from "react"; +import type { CaptchaProps } from "./captcha.js"; +import { componentsList } from "./componentsList.js"; + +class CaptchaRenderer { + public render( + captchaType: FeaturesEnum, + captchaProps: CaptchaProps, + ): ReactNode { + const CaptchaComponent = componentsList[captchaType]; + + console.log(`rendering ${captchaType}`); + + return ( + + ); + } +} + +export { CaptchaRenderer }; diff --git a/packages/procaptcha-bundle/src/util/renderLogic/captcha/components/frictionlessCaptcha.tsx b/packages/procaptcha-bundle/src/util/renderLogic/captcha/components/frictionlessCaptcha.tsx new file mode 100644 index 000000000..bcac86d78 --- /dev/null +++ b/packages/procaptcha-bundle/src/util/renderLogic/captcha/components/frictionlessCaptcha.tsx @@ -0,0 +1,26 @@ +// Copyright 2021-2024 Prosopo (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { ProcaptchaFrictionless } from "@prosopo/procaptcha-frictionless"; +import React from "react"; +import { CaptchaElement } from "../captcha.js"; + +class FrictionlessCaptcha extends CaptchaElement { + public override render() { + const { config, callbacks } = this.props; + + return ; + } +} + +export { FrictionlessCaptcha }; diff --git a/packages/procaptcha-bundle/src/util/renderLogic/captcha/components/imageCaptcha.tsx b/packages/procaptcha-bundle/src/util/renderLogic/captcha/components/imageCaptcha.tsx new file mode 100644 index 000000000..4acbdea04 --- /dev/null +++ b/packages/procaptcha-bundle/src/util/renderLogic/captcha/components/imageCaptcha.tsx @@ -0,0 +1,26 @@ +// Copyright 2021-2024 Prosopo (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { Procaptcha } from "@prosopo/procaptcha-react"; +import React from "react"; +import { CaptchaElement } from "../captcha.js"; + +class ImageCaptcha extends CaptchaElement { + public override render() { + const { config, callbacks } = this.props; + + return ; + } +} + +export { ImageCaptcha }; diff --git a/packages/procaptcha-bundle/src/util/renderLogic/captcha/components/powCaptcha.tsx b/packages/procaptcha-bundle/src/util/renderLogic/captcha/components/powCaptcha.tsx new file mode 100644 index 000000000..4b626e72d --- /dev/null +++ b/packages/procaptcha-bundle/src/util/renderLogic/captcha/components/powCaptcha.tsx @@ -0,0 +1,26 @@ +// Copyright 2021-2024 Prosopo (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { ProcaptchaPow } from "@prosopo/procaptcha-pow"; +import React from "react"; +import { CaptchaElement } from "../captcha.js"; + +class PowCaptcha extends CaptchaElement { + public override render() { + const { config, callbacks } = this.props; + + return ; + } +} + +export { PowCaptcha }; diff --git a/packages/procaptcha-bundle/src/util/renderLogic/captcha/componentsList.ts b/packages/procaptcha-bundle/src/util/renderLogic/captcha/componentsList.ts new file mode 100644 index 000000000..364cc3405 --- /dev/null +++ b/packages/procaptcha-bundle/src/util/renderLogic/captcha/componentsList.ts @@ -0,0 +1,30 @@ +// Copyright 2021-2024 Prosopo (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import { FeaturesEnum } from "@prosopo/types"; +import type React from "react"; +import type { CaptchaProps } from "./captcha.js"; +import { FrictionlessCaptcha } from "./components/frictionlessCaptcha.js"; +import { ImageCaptcha } from "./components/imageCaptcha.js"; +import { PowCaptcha } from "./components/powCaptcha.js"; + +const componentsList: Record< + FeaturesEnum, + React.ComponentType +> = { + [FeaturesEnum.Image]: ImageCaptcha, + [FeaturesEnum.Pow]: PowCaptcha, + [FeaturesEnum.Frictionless]: FrictionlessCaptcha, +}; + +export { componentsList }; diff --git a/packages/procaptcha-bundle/src/util/renderLogic/webComponent.ts b/packages/procaptcha-bundle/src/util/renderLogic/webComponent.ts new file mode 100644 index 000000000..01b9e778f --- /dev/null +++ b/packages/procaptcha-bundle/src/util/renderLogic/webComponent.ts @@ -0,0 +1,49 @@ +// Copyright 2021-2024 Prosopo (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +class WebComponent { + public addToElement(element: Element): ShadowRoot { + const webComponent = this.makeWebComponent(); + const shadowRoot = this.addShadowDom(webComponent); + + element.appendChild(webComponent); + + return shadowRoot; + } + + protected makeWebComponent(): HTMLElement { + return document.createElement("prosopo-procaptcha"); + } + + protected getBaseShadowStyles(): string { + // todo maybe introduce customCSS in renderOptions. + const customCss = ""; + + let baseStyles = + ''; + baseStyles += "" !== customCss ? `` : ""; + + return baseStyles; + } + + protected addShadowDom(webComponent: HTMLElement): ShadowRoot { + // fixme check if shadowRoot is already attached. + const shadowRoot = webComponent.attachShadow({ mode: "open" }); + + shadowRoot.innerHTML += this.getBaseShadowStyles(); + + return shadowRoot; + } +} + +export { WebComponent }; diff --git a/packages/procaptcha-bundle/src/util/renderLogic/widgetRenderer.tsx b/packages/procaptcha-bundle/src/util/renderLogic/widgetRenderer.tsx new file mode 100644 index 000000000..c877cf169 --- /dev/null +++ b/packages/procaptcha-bundle/src/util/renderLogic/widgetRenderer.tsx @@ -0,0 +1,108 @@ +// Copyright 2021-2024 Prosopo (UK) Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +import createCache, { type EmotionCache } from "@emotion/cache"; +import { CacheProvider } from "@emotion/react"; +import type { + ProcaptchaClientConfigOutput, + ProcaptchaRenderOptions, +} from "@prosopo/types"; +import type { FeaturesEnum } from "@prosopo/types"; +import { type Root, createRoot } from "react-dom/client"; +import { + type Callbacks, + getDefaultCallbacks, + setUserCallbacks, +} from "../defaultCallbacks.js"; +import { setLanguage } from "../language.js"; +import { setTheme } from "../theme.js"; +import { setValidChallengeLength } from "../timeout.js"; +import type { CaptchaRenderer } from "./captcha/captchaRenderer.js"; +import type { WebComponent } from "./webComponent.js"; + +interface RenderSettings { + identifierPrefix: string; + defaultCaptchaType: FeaturesEnum; +} + +class WidgetRenderer { + private readonly webComponent: WebComponent; + private readonly captchaRenderer: CaptchaRenderer; + + constructor(webComponent: WebComponent, captchaRenderer: CaptchaRenderer) { + this.webComponent = webComponent; + this.captchaRenderer = captchaRenderer; + } + + public renderElements( + settings: RenderSettings, + elements: Element[], + config: ProcaptchaClientConfigOutput, + renderOptions?: ProcaptchaRenderOptions, + ): Root[] { + return elements.map((element) => { + return this.renderElement(settings, element, config, renderOptions); + }); + } + + protected renderElement( + settings: RenderSettings, + element: Element, + config: ProcaptchaClientConfigOutput, + renderOptions?: ProcaptchaRenderOptions, + ): Root { + const captchaType = + (renderOptions?.captchaType as FeaturesEnum) || + settings.defaultCaptchaType; + const callbacks = getDefaultCallbacks(element); + + this.readAndValidateSettings(element, callbacks, config, renderOptions); + + const shadowRoot = this.webComponent.addToElement(element); + const emotionCache = this.makeEmotionCache(shadowRoot); + const root = createRoot(shadowRoot, { + identifierPrefix: settings.identifierPrefix, + }); + + const captcha = this.captchaRenderer.render(captchaType, { + config: config, + callbacks: callbacks, + }); + + root.render({captcha}); + + return root; + } + + protected readAndValidateSettings( + element: Element, + callbacks: Callbacks, + config: ProcaptchaClientConfigOutput, + renderOptions?: ProcaptchaRenderOptions, + ): void { + setUserCallbacks(renderOptions, callbacks, element); + setTheme(renderOptions, element, config); + setValidChallengeLength(renderOptions, element, config); + setLanguage(renderOptions, element, config); + } + + protected makeEmotionCache(shadowRoot: ShadowRoot): EmotionCache { + return createCache({ + key: "procaptcha", + prepend: true, + container: shadowRoot, + }); + } +} + +export { WidgetRenderer }; From 71c327baa5d8d050c976f7e27f77e5cd211ef993 Mon Sep 17 00:00:00 2001 From: Maxim Akimov Date: Mon, 9 Dec 2024 11:59:44 +0200 Subject: [PATCH 3/5] procaptcha-bundle: renderLogic --- for-devs.md | 76 ++++++++- .../src/util/renderLogic.tsx | 41 ++--- .../src/util/renderLogic/webComponent.ts | 47 +++--- .../src/util/renderLogic/widgetRenderer.tsx | 153 +++++++++--------- 4 files changed, 198 insertions(+), 119 deletions(-) diff --git a/for-devs.md b/for-devs.md index 7828b0363..d064486ee 100644 --- a/for-devs.md +++ b/for-devs.md @@ -1 +1,75 @@ -fixme \ No newline at end of file +## Developer Information + +This file is intended for developers. It provides details on the project's structure and instructions on how to work +with it. + +### 1. Fixed Node Version Warning + +The project requires Node.js version 20 and is not compatible with later versions. You can +use [Node Version Manager (NVM)](https://github.com/nvm-sh/nvm) to manage your Node.js versions. + +### 2. Commands + +* Installation: `npm install` +* Building packages: `npm run build:all` +* Building the bundle: `npm run build:bundle` +* Linting: `npm run lint-fix` (formatting & code validation) + +### 3. Environment Setup for Tests (Once) + +To set up the environment for testing, run the following commands: + +``` +cp demos/client-example-server/env.development demos/client-example-server/.env.test +cp demos/client-example/env.development demos/client-example/.env.test +cp demos/client-bundle-example/env.development demos/client-bundle-example/.env.test +cp dev/scripts/env.test .env.test +cp dev/scripts/env.test dev/scripts/.env.test +cp dev/scripts/env.test packages/cli/.env.test +cp dev/scripts/env.test packages/procaptcha-bundle/.env.test +NODE_ENV="test" npm run setup +``` + +### 4. E2E Client Tests + +#### 4.1) Launching services + +The necessary services are docked. To start them, run the following: + +``` +docker compose --file ./docker/docker-compose.test.yml up -d --remove-orphans --force-recreate --always-recreate-deps +npm run -w @prosopo/client-example-server build && NODE_ENV=test npm run start:server +NODE_ENV=test npm run start:demo +NODE_ENV=test npm run start:provider:admin +``` + +#### 4.2) Running the Tests + +``` +NODE_ENV=test npm run -w @prosopo/cypress-shared cypress:open:client-example +``` + +#### 4.3) Stopping Docker Services + +After the tests finish, stop the Docker services with: + +``` +docker compose --file ./docker/docker-compose.test.yml down +``` + +### 5. E2E Bundle Tests + +#### 5.1) Launching Services + +For bundle tests, use the same services as for the E2E client tests, plus the following: + +``` +NODE_ENV="development" npm -w @prosopo/procaptcha-bundle run bundle +NODE_ENV=test npm run start:bundle +``` + +#### 5.2) Running the Tests + +``` +NODE_ENV=test npm -w @prosopo/cypress-shared run cypress:open:client-bundle-example +``` diff --git a/packages/procaptcha-bundle/src/util/renderLogic.tsx b/packages/procaptcha-bundle/src/util/renderLogic.tsx index 4f366e15f..771391309 100644 --- a/packages/procaptcha-bundle/src/util/renderLogic.tsx +++ b/packages/procaptcha-bundle/src/util/renderLogic.tsx @@ -12,31 +12,32 @@ // See the License for the specific language governing permissions and // limitations under the License. import { - FeaturesEnum, - type ProcaptchaClientConfigOutput, - type ProcaptchaRenderOptions, + FeaturesEnum, + type ProcaptchaClientConfigOutput, + type ProcaptchaRenderOptions, } from "@prosopo/types"; -import { CaptchaRenderer } from "./renderLogic/captcha/captchaRenderer.js"; -import { WebComponent } from "./renderLogic/webComponent.js"; -import { WidgetRenderer } from "./renderLogic/widgetRenderer.js"; +import {CaptchaRenderer} from "./renderLogic/captcha/captchaRenderer.js"; +import {WebComponent} from "./renderLogic/webComponent.js"; +import {WidgetRenderer} from "./renderLogic/widgetRenderer.js"; const widgetRenderer = new WidgetRenderer( - new WebComponent(), - new CaptchaRenderer(), + new WebComponent(), + new CaptchaRenderer(), ); export const renderLogic = ( - elements: Element[], - config: ProcaptchaClientConfigOutput, - renderOptions?: ProcaptchaRenderOptions, + elements: Element[], + config: ProcaptchaClientConfigOutput, + renderOptions?: ProcaptchaRenderOptions, ) => { - return widgetRenderer.renderElements( - { - identifierPrefix: "procaptcha-", - defaultCaptchaType: FeaturesEnum.Frictionless, - }, - elements, - config, - renderOptions, - ); + return widgetRenderer.renderElements( + { + identifierPrefix: "procaptcha-", + webComponentTag: "prosopo-procaptcha", + defaultCaptchaType: FeaturesEnum.Frictionless, + }, + elements, + config, + renderOptions, + ); }; diff --git a/packages/procaptcha-bundle/src/util/renderLogic/webComponent.ts b/packages/procaptcha-bundle/src/util/renderLogic/webComponent.ts index 01b9e778f..01089ea47 100644 --- a/packages/procaptcha-bundle/src/util/renderLogic/webComponent.ts +++ b/packages/procaptcha-bundle/src/util/renderLogic/webComponent.ts @@ -12,38 +12,37 @@ // See the License for the specific language governing permissions and // limitations under the License. class WebComponent { - public addToElement(element: Element): ShadowRoot { - const webComponent = this.makeWebComponent(); - const shadowRoot = this.addShadowDom(webComponent); + public addToElement(componentTag: string, element: Element): ShadowRoot { + const webComponent = this.makeWebComponent(componentTag); + const shadowRoot = this.addShadowDom(webComponent); - element.appendChild(webComponent); + element.appendChild(webComponent); - return shadowRoot; - } + return shadowRoot; + } - protected makeWebComponent(): HTMLElement { - return document.createElement("prosopo-procaptcha"); - } + protected makeWebComponent(componentTag: string): HTMLElement { + return document.createElement(componentTag); + } - protected getBaseShadowStyles(): string { - // todo maybe introduce customCSS in renderOptions. - const customCss = ""; + protected getBaseShadowStyles(): string { + // todo maybe introduce customCSS in renderOptions. + const customCss = ""; - let baseStyles = - ''; - baseStyles += "" !== customCss ? `` : ""; + let baseStyles = + ''; + baseStyles += "" !== customCss ? `` : ""; - return baseStyles; - } + return baseStyles; + } - protected addShadowDom(webComponent: HTMLElement): ShadowRoot { - // fixme check if shadowRoot is already attached. - const shadowRoot = webComponent.attachShadow({ mode: "open" }); + protected addShadowDom(webComponent: HTMLElement): ShadowRoot { + const shadowRoot = webComponent.attachShadow({mode: "open"}); - shadowRoot.innerHTML += this.getBaseShadowStyles(); + shadowRoot.innerHTML += this.getBaseShadowStyles(); - return shadowRoot; - } + return shadowRoot; + } } -export { WebComponent }; +export {WebComponent}; diff --git a/packages/procaptcha-bundle/src/util/renderLogic/widgetRenderer.tsx b/packages/procaptcha-bundle/src/util/renderLogic/widgetRenderer.tsx index c877cf169..e902c07fb 100644 --- a/packages/procaptcha-bundle/src/util/renderLogic/widgetRenderer.tsx +++ b/packages/procaptcha-bundle/src/util/renderLogic/widgetRenderer.tsx @@ -11,98 +11,103 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -import createCache, { type EmotionCache } from "@emotion/cache"; -import { CacheProvider } from "@emotion/react"; +import createCache, {type EmotionCache} from "@emotion/cache"; +import {CacheProvider} from "@emotion/react"; import type { - ProcaptchaClientConfigOutput, - ProcaptchaRenderOptions, + ProcaptchaClientConfigOutput, + ProcaptchaRenderOptions, } from "@prosopo/types"; -import type { FeaturesEnum } from "@prosopo/types"; -import { type Root, createRoot } from "react-dom/client"; +import type {FeaturesEnum} from "@prosopo/types"; +import {type Root, createRoot} from "react-dom/client"; import { - type Callbacks, - getDefaultCallbacks, - setUserCallbacks, + type Callbacks, + getDefaultCallbacks, + setUserCallbacks, } from "../defaultCallbacks.js"; -import { setLanguage } from "../language.js"; -import { setTheme } from "../theme.js"; -import { setValidChallengeLength } from "../timeout.js"; -import type { CaptchaRenderer } from "./captcha/captchaRenderer.js"; -import type { WebComponent } from "./webComponent.js"; +import {setLanguage} from "../language.js"; +import {setTheme} from "../theme.js"; +import {setValidChallengeLength} from "../timeout.js"; +import type {CaptchaRenderer} from "./captcha/captchaRenderer.js"; +import type {WebComponent} from "./webComponent.js"; interface RenderSettings { - identifierPrefix: string; - defaultCaptchaType: FeaturesEnum; + identifierPrefix: string; + webComponentTag: string; + defaultCaptchaType: FeaturesEnum; } class WidgetRenderer { - private readonly webComponent: WebComponent; - private readonly captchaRenderer: CaptchaRenderer; + private readonly webComponent: WebComponent; + private readonly captchaRenderer: CaptchaRenderer; - constructor(webComponent: WebComponent, captchaRenderer: CaptchaRenderer) { - this.webComponent = webComponent; - this.captchaRenderer = captchaRenderer; - } + constructor(webComponent: WebComponent, captchaRenderer: CaptchaRenderer) { + this.webComponent = webComponent; + this.captchaRenderer = captchaRenderer; + } - public renderElements( - settings: RenderSettings, - elements: Element[], - config: ProcaptchaClientConfigOutput, - renderOptions?: ProcaptchaRenderOptions, - ): Root[] { - return elements.map((element) => { - return this.renderElement(settings, element, config, renderOptions); - }); - } + public renderElements( + settings: RenderSettings, + elements: Element[], + config: ProcaptchaClientConfigOutput, + renderOptions?: ProcaptchaRenderOptions, + ): Root[] { + return elements.map((element) => { + return this.renderElement(settings, element, config, renderOptions); + }); + } - protected renderElement( - settings: RenderSettings, - element: Element, - config: ProcaptchaClientConfigOutput, - renderOptions?: ProcaptchaRenderOptions, - ): Root { - const captchaType = - (renderOptions?.captchaType as FeaturesEnum) || - settings.defaultCaptchaType; - const callbacks = getDefaultCallbacks(element); + protected renderElement( + settings: RenderSettings, + element: Element, + config: ProcaptchaClientConfigOutput, + renderOptions?: ProcaptchaRenderOptions, + ): Root { + const captchaType = + (renderOptions?.captchaType as FeaturesEnum) || + settings.defaultCaptchaType; + const callbacks = getDefaultCallbacks(element); - this.readAndValidateSettings(element, callbacks, config, renderOptions); + this.readAndValidateSettings(element, callbacks, config, renderOptions); - const shadowRoot = this.webComponent.addToElement(element); - const emotionCache = this.makeEmotionCache(shadowRoot); - const root = createRoot(shadowRoot, { - identifierPrefix: settings.identifierPrefix, - }); + // Clear all the children inside, if there are any. + // If the renderElement() is called several times on the same element, it should recreate the captcha from scratch. + element.innerHTML = ''; - const captcha = this.captchaRenderer.render(captchaType, { - config: config, - callbacks: callbacks, - }); + const shadowRoot = this.webComponent.addToElement(settings.webComponentTag, element); + const emotionCache = this.makeEmotionCache(shadowRoot); + const root = createRoot(shadowRoot, { + identifierPrefix: settings.identifierPrefix, + }); - root.render({captcha}); + const captcha = this.captchaRenderer.render(captchaType, { + config: config, + callbacks: callbacks, + }); - return root; - } + root.render({captcha}); - protected readAndValidateSettings( - element: Element, - callbacks: Callbacks, - config: ProcaptchaClientConfigOutput, - renderOptions?: ProcaptchaRenderOptions, - ): void { - setUserCallbacks(renderOptions, callbacks, element); - setTheme(renderOptions, element, config); - setValidChallengeLength(renderOptions, element, config); - setLanguage(renderOptions, element, config); - } + return root; + } - protected makeEmotionCache(shadowRoot: ShadowRoot): EmotionCache { - return createCache({ - key: "procaptcha", - prepend: true, - container: shadowRoot, - }); - } + protected readAndValidateSettings( + element: Element, + callbacks: Callbacks, + config: ProcaptchaClientConfigOutput, + renderOptions?: ProcaptchaRenderOptions, + ): void { + setUserCallbacks(renderOptions, callbacks, element); + setTheme(renderOptions, element, config); + setValidChallengeLength(renderOptions, element, config); + setLanguage(renderOptions, element, config); + } + + protected makeEmotionCache(shadowRoot: ShadowRoot): EmotionCache { + return createCache({ + key: "procaptcha", + prepend: true, + container: shadowRoot, + }); + } } -export { WidgetRenderer }; +export {WidgetRenderer}; From e58f0d1199be6c3a0ac4d91eca44842afa929882 Mon Sep 17 00:00:00 2001 From: Maxim Akimov Date: Mon, 9 Dec 2024 12:19:13 +0200 Subject: [PATCH 4/5] procaptcha-bundle: renderLogic --- for-devs.md | 16 +- .../src/util/renderLogic.tsx | 43 ++--- .../src/util/renderLogic/webComponent.ts | 46 ++--- .../src/util/renderLogic/widgetRenderer.tsx | 166 ++++++++++-------- 4 files changed, 145 insertions(+), 126 deletions(-) diff --git a/for-devs.md b/for-devs.md index d064486ee..25fe649f4 100644 --- a/for-devs.md +++ b/for-devs.md @@ -27,17 +27,25 @@ cp dev/scripts/env.test .env.test cp dev/scripts/env.test dev/scripts/.env.test cp dev/scripts/env.test packages/cli/.env.test cp dev/scripts/env.test packages/procaptcha-bundle/.env.test -NODE_ENV="test" npm run setup ``` -### 4. E2E Client Tests +### 4. Running E2E Client Tests Locally #### 4.1) Launching services -The necessary services are docked. To start them, run the following: +The DB is docked, and to start the DB service, run the following: ``` docker compose --file ./docker/docker-compose.test.yml up -d --remove-orphans --force-recreate --always-recreate-deps +NODE_ENV="test" npm run setup +``` + +> Note: the second command should be called once per the container lifetime, and adds the initial data, like domains, +> siteKeys, etc. + +Then start the services: + +``` npm run -w @prosopo/client-example-server build && NODE_ENV=test npm run start:server NODE_ENV=test npm run start:demo NODE_ENV=test npm run start:provider:admin @@ -57,7 +65,7 @@ After the tests finish, stop the Docker services with: docker compose --file ./docker/docker-compose.test.yml down ``` -### 5. E2E Bundle Tests +### 5. Running E2E Bundle Tests Locally #### 5.1) Launching Services diff --git a/packages/procaptcha-bundle/src/util/renderLogic.tsx b/packages/procaptcha-bundle/src/util/renderLogic.tsx index 771391309..88bbfd2fa 100644 --- a/packages/procaptcha-bundle/src/util/renderLogic.tsx +++ b/packages/procaptcha-bundle/src/util/renderLogic.tsx @@ -12,32 +12,33 @@ // See the License for the specific language governing permissions and // limitations under the License. import { - FeaturesEnum, - type ProcaptchaClientConfigOutput, - type ProcaptchaRenderOptions, + FeaturesEnum, + type ProcaptchaClientConfigOutput, + type ProcaptchaRenderOptions, } from "@prosopo/types"; -import {CaptchaRenderer} from "./renderLogic/captcha/captchaRenderer.js"; -import {WebComponent} from "./renderLogic/webComponent.js"; -import {WidgetRenderer} from "./renderLogic/widgetRenderer.js"; +import { CaptchaRenderer } from "./renderLogic/captcha/captchaRenderer.js"; +import { WebComponent } from "./renderLogic/webComponent.js"; +import { WidgetRenderer } from "./renderLogic/widgetRenderer.js"; const widgetRenderer = new WidgetRenderer( - new WebComponent(), - new CaptchaRenderer(), + new WebComponent(), + new CaptchaRenderer(), ); export const renderLogic = ( - elements: Element[], - config: ProcaptchaClientConfigOutput, - renderOptions?: ProcaptchaRenderOptions, + elements: Element[], + config: ProcaptchaClientConfigOutput, + renderOptions?: ProcaptchaRenderOptions, ) => { - return widgetRenderer.renderElements( - { - identifierPrefix: "procaptcha-", - webComponentTag: "prosopo-procaptcha", - defaultCaptchaType: FeaturesEnum.Frictionless, - }, - elements, - config, - renderOptions, - ); + return widgetRenderer.renderElements( + { + identifierPrefix: "procaptcha-", + emotionCacheKey: "procaptcha", + webComponentTag: "prosopo-procaptcha", + defaultCaptchaType: FeaturesEnum.Frictionless, + }, + elements, + config, + renderOptions, + ); }; diff --git a/packages/procaptcha-bundle/src/util/renderLogic/webComponent.ts b/packages/procaptcha-bundle/src/util/renderLogic/webComponent.ts index 01089ea47..61ef8d2cd 100644 --- a/packages/procaptcha-bundle/src/util/renderLogic/webComponent.ts +++ b/packages/procaptcha-bundle/src/util/renderLogic/webComponent.ts @@ -12,37 +12,37 @@ // See the License for the specific language governing permissions and // limitations under the License. class WebComponent { - public addToElement(componentTag: string, element: Element): ShadowRoot { - const webComponent = this.makeWebComponent(componentTag); - const shadowRoot = this.addShadowDom(webComponent); + public addToElement(componentTag: string, element: Element): ShadowRoot { + const webComponent = this.makeWebComponent(componentTag); + const shadowRoot = this.attachShadowDom(webComponent); - element.appendChild(webComponent); + element.appendChild(webComponent); - return shadowRoot; - } + return shadowRoot; + } - protected makeWebComponent(componentTag: string): HTMLElement { - return document.createElement(componentTag); - } + protected makeWebComponent(componentTag: string): HTMLElement { + return document.createElement(componentTag); + } - protected getBaseShadowStyles(): string { - // todo maybe introduce customCSS in renderOptions. - const customCss = ""; + protected getBaseShadowStyles(): string { + // todo maybe introduce customCSS in renderOptions. + const customCss = ""; - let baseStyles = - ''; - baseStyles += "" !== customCss ? `` : ""; + let baseStyles = + ''; + baseStyles += "" !== customCss ? `` : ""; - return baseStyles; - } + return baseStyles; + } - protected addShadowDom(webComponent: HTMLElement): ShadowRoot { - const shadowRoot = webComponent.attachShadow({mode: "open"}); + protected attachShadowDom(webComponent: HTMLElement): ShadowRoot { + const shadowRoot = webComponent.attachShadow({ mode: "open" }); - shadowRoot.innerHTML += this.getBaseShadowStyles(); + shadowRoot.innerHTML += this.getBaseShadowStyles(); - return shadowRoot; - } + return shadowRoot; + } } -export {WebComponent}; +export { WebComponent }; diff --git a/packages/procaptcha-bundle/src/util/renderLogic/widgetRenderer.tsx b/packages/procaptcha-bundle/src/util/renderLogic/widgetRenderer.tsx index e902c07fb..248eca594 100644 --- a/packages/procaptcha-bundle/src/util/renderLogic/widgetRenderer.tsx +++ b/packages/procaptcha-bundle/src/util/renderLogic/widgetRenderer.tsx @@ -11,103 +11,113 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -import createCache, {type EmotionCache} from "@emotion/cache"; -import {CacheProvider} from "@emotion/react"; +import createCache, { type EmotionCache } from "@emotion/cache"; +import { CacheProvider } from "@emotion/react"; import type { - ProcaptchaClientConfigOutput, - ProcaptchaRenderOptions, + ProcaptchaClientConfigOutput, + ProcaptchaRenderOptions, } from "@prosopo/types"; -import type {FeaturesEnum} from "@prosopo/types"; -import {type Root, createRoot} from "react-dom/client"; +import type { FeaturesEnum } from "@prosopo/types"; +import { type Root, createRoot } from "react-dom/client"; import { - type Callbacks, - getDefaultCallbacks, - setUserCallbacks, + type Callbacks, + getDefaultCallbacks, + setUserCallbacks, } from "../defaultCallbacks.js"; -import {setLanguage} from "../language.js"; -import {setTheme} from "../theme.js"; -import {setValidChallengeLength} from "../timeout.js"; -import type {CaptchaRenderer} from "./captcha/captchaRenderer.js"; -import type {WebComponent} from "./webComponent.js"; +import { setLanguage } from "../language.js"; +import { setTheme } from "../theme.js"; +import { setValidChallengeLength } from "../timeout.js"; +import type { CaptchaRenderer } from "./captcha/captchaRenderer.js"; +import type { WebComponent } from "./webComponent.js"; interface RenderSettings { - identifierPrefix: string; - webComponentTag: string; - defaultCaptchaType: FeaturesEnum; + identifierPrefix: string; + emotionCacheKey: string; + webComponentTag: string; + defaultCaptchaType: FeaturesEnum; } class WidgetRenderer { - private readonly webComponent: WebComponent; - private readonly captchaRenderer: CaptchaRenderer; + private readonly webComponent: WebComponent; + private readonly captchaRenderer: CaptchaRenderer; - constructor(webComponent: WebComponent, captchaRenderer: CaptchaRenderer) { - this.webComponent = webComponent; - this.captchaRenderer = captchaRenderer; - } + constructor(webComponent: WebComponent, captchaRenderer: CaptchaRenderer) { + this.webComponent = webComponent; + this.captchaRenderer = captchaRenderer; + } - public renderElements( - settings: RenderSettings, - elements: Element[], - config: ProcaptchaClientConfigOutput, - renderOptions?: ProcaptchaRenderOptions, - ): Root[] { - return elements.map((element) => { - return this.renderElement(settings, element, config, renderOptions); - }); - } + public renderElements( + settings: RenderSettings, + elements: Element[], + config: ProcaptchaClientConfigOutput, + renderOptions?: ProcaptchaRenderOptions, + ): Root[] { + return elements.map((element) => { + return this.renderElement(settings, element, config, renderOptions); + }); + } - protected renderElement( - settings: RenderSettings, - element: Element, - config: ProcaptchaClientConfigOutput, - renderOptions?: ProcaptchaRenderOptions, - ): Root { - const captchaType = - (renderOptions?.captchaType as FeaturesEnum) || - settings.defaultCaptchaType; - const callbacks = getDefaultCallbacks(element); + protected renderElement( + settings: RenderSettings, + element: Element, + config: ProcaptchaClientConfigOutput, + renderOptions?: ProcaptchaRenderOptions, + ): Root { + const captchaType = + (renderOptions?.captchaType as FeaturesEnum) || + settings.defaultCaptchaType; + const callbacks = getDefaultCallbacks(element); - this.readAndValidateSettings(element, callbacks, config, renderOptions); + this.readAndValidateSettings(element, callbacks, config, renderOptions); - // Clear all the children inside, if there are any. - // If the renderElement() is called several times on the same element, it should recreate the captcha from scratch. - element.innerHTML = ''; + // Clear all the children inside, if there are any. + // If the renderElement() is called several times on the same element, it should recreate the captcha from scratch. + element.innerHTML = ""; - const shadowRoot = this.webComponent.addToElement(settings.webComponentTag, element); - const emotionCache = this.makeEmotionCache(shadowRoot); - const root = createRoot(shadowRoot, { - identifierPrefix: settings.identifierPrefix, - }); + const shadowRoot = this.webComponent.addToElement( + settings.webComponentTag, + element, + ); + const emotionCache = this.makeEmotionCache( + settings.emotionCacheKey, + shadowRoot, + ); + const root = createRoot(shadowRoot, { + identifierPrefix: settings.identifierPrefix, + }); - const captcha = this.captchaRenderer.render(captchaType, { - config: config, - callbacks: callbacks, - }); + const captcha = this.captchaRenderer.render(captchaType, { + config: config, + callbacks: callbacks, + }); - root.render({captcha}); + root.render({captcha}); - return root; - } + return root; + } - protected readAndValidateSettings( - element: Element, - callbacks: Callbacks, - config: ProcaptchaClientConfigOutput, - renderOptions?: ProcaptchaRenderOptions, - ): void { - setUserCallbacks(renderOptions, callbacks, element); - setTheme(renderOptions, element, config); - setValidChallengeLength(renderOptions, element, config); - setLanguage(renderOptions, element, config); - } + protected readAndValidateSettings( + element: Element, + callbacks: Callbacks, + config: ProcaptchaClientConfigOutput, + renderOptions?: ProcaptchaRenderOptions, + ): void { + setUserCallbacks(renderOptions, callbacks, element); + setTheme(renderOptions, element, config); + setValidChallengeLength(renderOptions, element, config); + setLanguage(renderOptions, element, config); + } - protected makeEmotionCache(shadowRoot: ShadowRoot): EmotionCache { - return createCache({ - key: "procaptcha", - prepend: true, - container: shadowRoot, - }); - } + protected makeEmotionCache( + cacheKey: string, + shadowRoot: ShadowRoot, + ): EmotionCache { + return createCache({ + key: cacheKey, + prepend: true, + container: shadowRoot, + }); + } } -export {WidgetRenderer}; +export { WidgetRenderer }; From d311cd6c025402e256469e7b6b338399911eca85 Mon Sep 17 00:00:00 2001 From: Maxim Akimov Date: Tue, 10 Dec 2024 09:14:06 +0200 Subject: [PATCH 5/5] for-devs.md update --- for-devs.md | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/for-devs.md b/for-devs.md index 25fe649f4..212f99f6f 100644 --- a/for-devs.md +++ b/for-devs.md @@ -3,19 +3,14 @@ This file is intended for developers. It provides details on the project's structure and instructions on how to work with it. -### 1. Fixed Node Version Warning - -The project requires Node.js version 20 and is not compatible with later versions. You can -use [Node Version Manager (NVM)](https://github.com/nvm-sh/nvm) to manage your Node.js versions. - -### 2. Commands +### 1. Commands * Installation: `npm install` * Building packages: `npm run build:all` * Building the bundle: `npm run build:bundle` * Linting: `npm run lint-fix` (formatting & code validation) -### 3. Environment Setup for Tests (Once) +### 2. Environment Setup for Tests (Once) To set up the environment for testing, run the following commands: @@ -29,9 +24,9 @@ cp dev/scripts/env.test packages/cli/.env.test cp dev/scripts/env.test packages/procaptcha-bundle/.env.test ``` -### 4. Running E2E Client Tests Locally +### 3. Running E2E Client Tests Locally -#### 4.1) Launching services +#### 3.1) Launching services The DB is docked, and to start the DB service, run the following: @@ -51,13 +46,13 @@ NODE_ENV=test npm run start:demo NODE_ENV=test npm run start:provider:admin ``` -#### 4.2) Running the Tests +#### 3.2) Running the Tests ``` NODE_ENV=test npm run -w @prosopo/cypress-shared cypress:open:client-example ``` -#### 4.3) Stopping Docker Services +#### 3.3) Stopping Docker Services After the tests finish, stop the Docker services with: @@ -65,9 +60,9 @@ After the tests finish, stop the Docker services with: docker compose --file ./docker/docker-compose.test.yml down ``` -### 5. Running E2E Bundle Tests Locally +### 4. Running E2E Bundle Tests Locally -#### 5.1) Launching Services +#### 4.1) Launching Services For bundle tests, use the same services as for the E2E client tests, plus the following: @@ -76,7 +71,7 @@ NODE_ENV="development" npm -w @prosopo/procaptcha-bundle run bundle NODE_ENV=test npm run start:bundle ``` -#### 5.2) Running the Tests +#### 4.2) Running the Tests ``` NODE_ENV=test npm -w @prosopo/cypress-shared run cypress:open:client-bundle-example