diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 88a4e5c1aeca..3517881c4828 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -1,14 +1,28 @@
name: E2E Tests
+
on:
workflow_call:
+ secrets:
+ AZURE_CLIENT_ID:
+ required: false
+ AZURE_TENANT_ID:
+ required: false
+ AZURE_SUBSCRIPTION_ID:
+ required: false
+ PLAYWRIGHT_SERVICE_URL:
+ required: false
+ # Add other secrets if necessary
workflow_dispatch:
+env:
+ TELEMETRY_DISABLED: 1
+
permissions:
+ id-token: write
contents: read
actions: read
- checks: write
-env:
- TELEMETRY_DISABLED: 1
+ checks: write
+
jobs:
build:
name: Run E2E Tests
@@ -88,7 +102,31 @@ jobs:
- name: Install Playwright
run: pnpm exec playwright install --with-deps
- - name: Run E2E Tests
+ - name: Set Azure Secret Variables
+ run: |
+ if [[ -n "${{ secrets.AZURE_CLIENT_ID }}" && -n "${{ secrets.AZURE_TENANT_ID }}" && -n "${{ secrets.AZURE_SUBSCRIPTION_ID }}" ]]; then
+ echo "AZURE_ENABLED=true" >> $GITHUB_ENV
+ else
+ echo "AZURE_ENABLED=false" >> $GITHUB_ENV
+ fi
+
+ - name: Azure login
+ if: env.AZURE_ENABLED == 'true'
+ uses: azure/login@v2
+ with:
+ client-id: ${{ secrets.AZURE_CLIENT_ID }}
+ tenant-id: ${{ secrets.AZURE_TENANT_ID }}
+ subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
+
+ - name: Run E2E Tests (Azure)
+ if: env.AZURE_ENABLED == 'true'
+ env:
+ PLAYWRIGHT_SERVICE_URL: ${{ secrets.PLAYWRIGHT_SERVICE_URL }}
+ run: |
+ pnpm test-e2e:azure
+
+ - name: Run E2E Tests (Local)
+ if: env.AZURE_ENABLED == 'false'
run: |
pnpm test:e2e
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index 9ce7eca74f6b..ae703a4e4011 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -6,6 +6,7 @@ permissions:
pull-requests: read
actions: read
checks: write
+ id-token: write
on:
pull_request:
@@ -19,53 +20,28 @@ concurrency:
cancel-in-progress: true
jobs:
- changes:
- name: Detect changes
- runs-on: ubuntu-latest
- outputs:
- has-files-requiring-all-checks: ${{ steps.filter.outputs.has-files-requiring-all-checks }}
- steps:
- - uses: actions/checkout@v3
- - uses: ./.github/actions/dangerous-git-checkout
- - uses: dorny/paths-filter@v2
- id: filter
- with:
- filters: |
- has-files-requiring-all-checks:
- - "!(**.md|.github/CODEOWNERS)"
-
test:
name: Run Unit Tests
- needs: [changes]
- if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
uses: ./.github/workflows/test.yml
secrets: inherit
lint:
name: Run Linters
- needs: [changes]
- if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
uses: ./.github/workflows/lint.yml
secrets: inherit
build:
name: Build Formbricks-web
- needs: [changes]
- if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
uses: ./.github/workflows/build-web.yml
secrets: inherit
docs:
name: Build Docs
- needs: [changes]
- if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
uses: ./.github/workflows/build-docs.yml
secrets: inherit
e2e-test:
name: Run E2E Tests
- needs: [changes]
- if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
uses: ./.github/workflows/e2e.yml
secrets: inherit
diff --git a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MatrixQuestionForm.tsx b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MatrixQuestionForm.tsx
index 3788beba3669..fe6f46d92f49 100644
--- a/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MatrixQuestionForm.tsx
+++ b/apps/web/app/(app)/(survey-editor)/environments/[environmentId]/surveys/[surveyId]/edit/components/MatrixQuestionForm.tsx
@@ -166,7 +166,7 @@ export const MatrixQuestionForm = ({
handleKeyDown(e, "row")}
- key={`row-${index}-${question.rows.length}`}>
+ key={`row-${index}`}>
handleKeyDown(e, "column")}
- key={`column-${index}-${question.columns.length}`}>
+ key={`column-${index}`}>
+
+
+
+
+ This is my sample page using the Formbricks JS javascript widget
+
+`;
test.describe("JS Package Test", async () => {
+ let server: http.Server;
let environmentId: string;
- test("Tests", async ({ page, users }) => {
- await test.step("Admin creates an In-App Survey", async () => {
- const user = await users.create();
- await user.login();
-
- await page.waitForURL(/\/environments\/[^/]+\/surveys/);
-
- await page.getByRole("heading", { name: "Product Market Fit (Superhuman)" }).isVisible();
- await page.getByRole("heading", { name: "Product Market Fit (Superhuman)" }).click();
-
- await page.getByRole("button", { name: "Use this template" }).isVisible();
- await page.getByRole("button", { name: "Use this template" }).click();
-
- await page.waitForURL(/\/environments\/[^/]+\/surveys\/[^/]+\/edit/);
-
- await page.getByRole("button", { name: "Settings", exact: true }).click();
-
- await expect(page.locator("#howToSendCardTrigger")).toBeVisible();
- await page.locator("#howToSendCardTrigger").click();
-
- await expect(page.locator("#howToSendCardOption-app")).toBeVisible();
- await page.locator("#howToSendCardOption-app").click();
+ test.beforeAll(async () => {
+ // Create a simple HTTP server
+ server = http.createServer((_, res) => {
+ const htmlContent = HTML_TEMPLATE.replace("ENVIRONMENT_ID", environmentId || "");
+ res.writeHead(200, { "Content-Type": "text/html" });
+ res.end(htmlContent);
+ });
- await page.locator("#whenToSendCardTrigger").click();
+ await new Promise((resolve) => {
+ server.listen(3004, () => resolve());
+ });
+ });
- await page.getByRole("button", { name: "Add action" }).click();
- await page.getByText("New SessionGets fired when a").click();
+ test.afterAll(async () => {
+ // Cleanup: close the server
+ await new Promise((resolve) => server.close(resolve));
+ });
- await page.locator("#recontactOptionsCardTrigger").click();
+ test("Create, display and validate PMF survey", async ({ page, users }) => {
+ // Create and login user
+ const user = await users.create();
+ await user.login();
- await page.locator("label").filter({ hasText: "Keep showing while conditions" }).click();
- await page.locator("#recontactDays").check();
+ await page.waitForURL(/\/environments\/[^/]+\/surveys/);
- await page.getByRole("button", { name: "Publish" }).click();
+ // Extract environmentId early in the test
+ environmentId =
+ /\/environments\/([^/]+)\/surveys/.exec(page.url())?.[1] ??
+ (() => {
+ throw new Error("Unable to parse environmentId from URL");
+ })();
- environmentId =
- /\/environments\/([^/]+)\/surveys/.exec(page.url())?.[1] ??
- (() => {
- throw new Error("Unable to parse environmentId from URL");
- })();
+ // Create survey from template
+ await page.getByRole("heading", { name: "Product Market Fit (Superhuman)" }).isVisible();
+ await page.getByRole("heading", { name: "Product Market Fit (Superhuman)" }).click();
+ await page.getByRole("button", { name: "Use this template" }).isVisible();
+ await page.getByRole("button", { name: "Use this template" }).click();
- await page.waitForURL(/\/environments\/[^/]+\/surveys\/[^/]+\/summary/);
- });
+ // Configure survey settings
+ await page.waitForURL(/\/environments\/[^/]+\/surveys\/[^/]+\/edit/);
+ await page.getByRole("button", { name: "Settings", exact: true }).click();
- await test.step("JS display survey on page and submit response", async () => {
- let currentDir = process.cwd();
- let htmlFilePath = currentDir + "/packages/js/index.html";
-
- let htmlFile = replaceEnvironmentIdInHtml(htmlFilePath, environmentId);
- await page.goto(htmlFile);
-
- // Formbricks In App Sync has happened
- const syncApi = await page.waitForResponse(
- (response) => {
- return response.url().includes("/environment");
- },
- {
- timeout: 120000,
- }
- );
-
- expect(syncApi.status()).toBe(200);
-
- // Formbricks Modal exists in the DOM
- await expect(page.locator("#formbricks-modal-container")).toHaveCount(1);
-
- // Formbricks Modal is visible
- await expect(
- page.locator("#questionCard-0").getByRole("link", { name: "Powered by Formbricks" })
- ).toBeVisible();
-
- // Fill the Survey
- await page.getByRole("button", { name: "Happy to help!" }).click();
- await page.locator("label").filter({ hasText: "Somewhat disappointed" }).click();
- await page.locator("#questionCard-1").getByRole("button", { name: "Next" }).click();
- await page.locator("label").filter({ hasText: "Founder" }).click();
- await page.locator("#questionCard-2").getByRole("button", { name: "Next" }).click();
- await page
- .locator("#questionCard-3")
- .getByLabel("textarea")
- .fill("People who believe that PMF is necessary");
- await page.locator("#questionCard-3").getByRole("button", { name: "Next" }).click();
- await page.locator("#questionCard-4").getByLabel("textarea").fill("Much higher response rates!");
- await page.locator("#questionCard-4").getByRole("button", { name: "Next" }).click();
- await page.locator("#questionCard-5").getByLabel("textarea").fill("Make this end to end test pass!");
- await page.getByRole("button", { name: "Finish" }).click();
-
- // loading spinner -> wait for it to disappear
- await page.getByTestId("loading-spinner").waitFor({ state: "hidden" });
- await page.waitForLoadState("networkidle");
- });
+ await expect(page.locator("#howToSendCardTrigger")).toBeVisible();
+ await page.locator("#howToSendCardTrigger").click();
+ await expect(page.locator("#howToSendCardOption-app")).toBeVisible();
+ await page.locator("#howToSendCardOption-app").click();
- await test.step("Admin validates Displays & Response", async () => {
- await page.goto("/");
- await page.waitForURL(/\/environments\/[^/]+\/surveys/);
+ await page.locator("#whenToSendCardTrigger").click();
+ await page.getByRole("button", { name: "Add action" }).click();
+ await page.getByText("New SessionGets fired when a").click();
- await page.getByRole("link", { name: "product Market Fit (Superhuman)" }).click();
- (await page.waitForSelector("text=Responses")).isVisible();
+ await page.locator("#recontactOptionsCardTrigger").click();
+ await page.locator("label").filter({ hasText: "Keep showing while conditions" }).click();
+ await page.locator("#recontactDays").check();
- await page.waitForLoadState("networkidle");
- await page.waitForTimeout(2000);
+ await page.getByRole("button", { name: "Publish" }).click();
- const impressionsCount = await page.getByRole("button", { name: "Impressions" }).innerText();
- expect(impressionsCount).toEqual("Impressions\n\n1");
+ await page.waitForURL(/\/environments\/[^/]+\/surveys\/[^/]+\/summary/);
- await expect(page.getByRole("link", { name: "Responses (1)" })).toBeVisible();
- await expect(page.getByRole("button", { name: "Completed 100%" })).toBeVisible();
+ // No need for file operations anymore, just use the server
+ await page.goto("http://localhost:3004");
- await expect(page.getByText("1 Responses", { exact: true }).first()).toBeVisible();
- await expect(page.getByText("CTR100%")).toBeVisible();
- await expect(page.getByText("Somewhat disappointed100%")).toBeVisible();
- await expect(page.getByText("Founder100%")).toBeVisible();
- await expect(page.getByText("People who believe that PMF").first()).toBeVisible();
- await expect(page.getByText("Much higher response rates!").first()).toBeVisible();
- await expect(page.getByText("Make this end to end test").first()).toBeVisible();
+ const syncApi = await page.waitForResponse((response) => response.url().includes("/environment"), {
+ timeout: 120000,
});
+ expect(syncApi.status()).toBe(200);
+
+ await expect(page.locator("#formbricks-modal-container")).toHaveCount(1);
+ await expect(
+ page.locator("#questionCard-0").getByRole("link", { name: "Powered by Formbricks" })
+ ).toBeVisible();
+
+ // Fill the survey
+ await page.getByRole("button", { name: "Happy to help!" }).click();
+ await page.locator("label").filter({ hasText: "Somewhat disappointed" }).click();
+ await page.locator("#questionCard-1").getByRole("button", { name: "Next" }).click();
+ await page.locator("label").filter({ hasText: "Founder" }).click();
+ await page.locator("#questionCard-2").getByRole("button", { name: "Next" }).click();
+ await page
+ .locator("#questionCard-3")
+ .getByLabel("textarea")
+ .fill("People who believe that PMF is necessary");
+ await page.locator("#questionCard-3").getByRole("button", { name: "Next" }).click();
+ await page.locator("#questionCard-4").getByLabel("textarea").fill("Much higher response rates!");
+ await page.locator("#questionCard-4").getByRole("button", { name: "Next" }).click();
+ await page.locator("#questionCard-5").getByLabel("textarea").fill("Make this end to end test pass!");
+ await page.getByRole("button", { name: "Finish" }).click();
+
+ await page.getByTestId("loading-spinner").waitFor({ state: "hidden" });
+ await page.waitForLoadState("networkidle");
+
+ // Validate displays and response
+ await page.goto("/");
+ await page.waitForURL(/\/environments\/[^/]+\/surveys/);
+ await page.getByRole("link", { name: "product Market Fit (Superhuman)" }).click();
+ await page.waitForSelector("text=Responses");
+ await page.waitForLoadState("networkidle");
+ await page.waitForTimeout(2000);
+
+ const impressionsCount = await page.getByRole("button", { name: "Impressions" }).innerText();
+ expect(impressionsCount).toEqual("Impressions\n\n1");
+
+ await expect(page.getByRole("link", { name: "Responses (1)" })).toBeVisible();
+ await expect(page.getByRole("button", { name: "Completed 100%" })).toBeVisible();
+ await expect(page.getByText("1 Responses", { exact: true }).first()).toBeVisible();
+ await expect(page.getByText("CTR100%")).toBeVisible();
+ await expect(page.getByText("Somewhat disappointed100%")).toBeVisible();
+ await expect(page.getByText("Founder100%")).toBeVisible();
+ await expect(page.getByText("People who believe that PMF").first()).toBeVisible();
+ await expect(page.getByText("Much higher response rates!").first()).toBeVisible();
+ await expect(page.getByText("Make this end to end test").first()).toBeVisible();
});
});
diff --git a/apps/web/playwright/survey.spec.ts b/apps/web/playwright/survey.spec.ts
index 8e2487242ff1..9529c9289bf7 100644
--- a/apps/web/playwright/survey.spec.ts
+++ b/apps/web/playwright/survey.spec.ts
@@ -12,8 +12,6 @@ test.use({
test.describe("Survey Create & Submit Response without logic", async () => {
let url: string | null;
- test.slow();
-
test("Create survey and submit response", async ({ page, users }) => {
const user = await users.create();
await user.login();
diff --git a/package.json b/package.json
index 8908063a9b80..ef02f4683eb2 100644
--- a/package.json
+++ b/package.json
@@ -27,11 +27,13 @@
"release": "turbo run build --filter=@formbricks/js... && changeset publish",
"test": "turbo run test --no-cache",
"test:e2e": "playwright test",
+ "test-e2e:azure": "pnpm test:e2e -c playwright.service.config.ts --workers=20",
"prepare": "husky install",
"storybook": "turbo run storybook",
"fb-migrate-dev": "pnpm --filter @formbricks/database create-migration && pnpm prisma generate"
},
"devDependencies": {
+ "@azure/microsoft-playwright-testing": "1.0.0-beta.6",
"@formbricks/eslint-config": "workspace:*",
"@playwright/test": "1.49.1",
"eslint": "8.57.0",
diff --git a/packages/js/index.html b/packages/js/index.html
deleted file mode 100644
index 542f990bbce1..000000000000
--- a/packages/js/index.html
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
- This is my sample page using the Formbricks JS javascript widget
-
diff --git a/playwright.service.config.ts b/playwright.service.config.ts
new file mode 100644
index 000000000000..1b73f41fd8e1
--- /dev/null
+++ b/playwright.service.config.ts
@@ -0,0 +1,22 @@
+import { ServiceOS, getServiceConfig } from "@azure/microsoft-playwright-testing";
+import { defineConfig } from "@playwright/test";
+import config from "./playwright.config";
+
+/* Learn more about service configuration at https://aka.ms/mpt/config */
+export default defineConfig(
+ config,
+ getServiceConfig(config, {
+ exposeNetwork: "",
+ timeout: 30000,
+ os: ServiceOS.LINUX,
+ useCloudHostedBrowsers: true, // Set to false if you want to only use reporting and not cloud hosted browsers
+ }),
+ {
+ /*
+ Playwright Testing service reporter is added by default.
+ This will override any reporter options specified in the base playwright config.
+ If you are using more reporters, please update your configuration accordingly.
+ */
+ reporter: [["list"], ["@azure/microsoft-playwright-testing/reporter"]],
+ }
+);
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 186336654ef1..916ab22514db 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -12,6 +12,9 @@ importers:
specifier: 2.27.10
version: 2.27.10
devDependencies:
+ '@azure/microsoft-playwright-testing':
+ specifier: 1.0.0-beta.6
+ version: 1.0.0-beta.6(@playwright/test@1.49.1)
'@formbricks/eslint-config':
specifier: workspace:*
version: link:packages/config-eslint
@@ -1454,6 +1457,76 @@ packages:
resolution: {integrity: sha512-2GPCwlNxeHspoK/Mc8nbk9cBOkSpp3j2SJUQmFnyQK6V/pR6II2oPRyZkMomug1Rc10hqlBHByMecq4zhV2uUw==}
engines: {node: '>=16.0.0'}
+ '@azure/abort-controller@2.1.2':
+ resolution: {integrity: sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==}
+ engines: {node: '>=18.0.0'}
+
+ '@azure/core-auth@1.9.0':
+ resolution: {integrity: sha512-FPwHpZywuyasDSLMqJ6fhbOK3TqUdviZNF8OqRGA4W5Ewib2lEEZ+pBsYcBa88B2NGO/SEnYPGhyBqNlE8ilSw==}
+ engines: {node: '>=18.0.0'}
+
+ '@azure/core-client@1.9.2':
+ resolution: {integrity: sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==}
+ engines: {node: '>=18.0.0'}
+
+ '@azure/core-http-compat@2.1.2':
+ resolution: {integrity: sha512-5MnV1yqzZwgNLLjlizsU3QqOeQChkIXw781Fwh1xdAqJR5AA32IUaq6xv1BICJvfbHoa+JYcaij2HFkhLbNTJQ==}
+ engines: {node: '>=18.0.0'}
+
+ '@azure/core-lro@2.7.2':
+ resolution: {integrity: sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==}
+ engines: {node: '>=18.0.0'}
+
+ '@azure/core-paging@1.6.2':
+ resolution: {integrity: sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==}
+ engines: {node: '>=18.0.0'}
+
+ '@azure/core-rest-pipeline@1.18.2':
+ resolution: {integrity: sha512-IkTf/DWKyCklEtN/WYW3lqEsIaUDshlzWRlZNNwSYtFcCBQz++OtOjxNpm8rr1VcbMS6RpjybQa3u6B6nG0zNw==}
+ engines: {node: '>=18.0.0'}
+
+ '@azure/core-tracing@1.2.0':
+ resolution: {integrity: sha512-UKTiEJPkWcESPYJz3X5uKRYyOcJD+4nYph+KpfdPRnQJVrZfk0KJgdnaAWKfhsBBtAf/D58Az4AvCJEmWgIBAg==}
+ engines: {node: '>=18.0.0'}
+
+ '@azure/core-util@1.11.0':
+ resolution: {integrity: sha512-DxOSLua+NdpWoSqULhjDyAZTXFdP/LKkqtYuxxz1SCN289zk3OG8UOpnCQAz/tygyACBtWp/BoO72ptK7msY8g==}
+ engines: {node: '>=18.0.0'}
+
+ '@azure/core-xml@1.4.4':
+ resolution: {integrity: sha512-J4FYAqakGXcbfeZjwjMzjNcpcH4E+JtEBv+xcV1yL0Ydn/6wbQfeFKTCHh9wttAi0lmajHw7yBbHPRG+YHckZQ==}
+ engines: {node: '>=18.0.0'}
+
+ '@azure/identity@4.5.0':
+ resolution: {integrity: sha512-EknvVmtBuSIic47xkOqyNabAme0RYTw52BTMz8eBgU1ysTyMrD1uOoM+JdS0J/4Yfp98IBT3osqq3BfwSaNaGQ==}
+ engines: {node: '>=18.0.0'}
+
+ '@azure/logger@1.1.4':
+ resolution: {integrity: sha512-4IXXzcCdLdlXuCG+8UKEwLA1T1NHqUfanhXYHiQTn+6sfWCZXduqbtXDGceg3Ce5QxTGo7EqmbV6Bi+aqKuClQ==}
+ engines: {node: '>=18.0.0'}
+
+ '@azure/microsoft-playwright-testing@1.0.0-beta.6':
+ resolution: {integrity: sha512-TVfBM5AL1t88VGNRfj6cudEYURVc4Y/n0tnL+o+TDL9OpAvGa8fZ2xPjXy6imjnX9dgpNjbowMG4pxCKo00OvA==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ '@playwright/test': ^1.43.1
+
+ '@azure/msal-browser@3.28.0':
+ resolution: {integrity: sha512-1c1qUF6vB52mWlyoMem4xR1gdwiQWYEQB2uhDkbAL4wVJr8WmAcXybc1Qs33y19N4BdPI8/DHI7rPE8L5jMtWw==}
+ engines: {node: '>=0.8.0'}
+
+ '@azure/msal-common@14.16.0':
+ resolution: {integrity: sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA==}
+ engines: {node: '>=0.8.0'}
+
+ '@azure/msal-node@2.16.2':
+ resolution: {integrity: sha512-An7l1hEr0w1HMMh1LU+rtDtqL7/jw74ORlc9Wnh06v7TU/xpG39/Zdr1ZJu3QpjUfKJ+E0/OXMW8DRSWTlh7qQ==}
+ engines: {node: '>=16'}
+
+ '@azure/storage-blob@12.26.0':
+ resolution: {integrity: sha512-SriLPKezypIsiZ+TtlFfE46uuBIap2HeaQVS78e1P7rz5OSbq0rsd52WE1mC5f7vAeLiXqv7I7oRhL3WFZEw3Q==}
+ engines: {node: '>=18.0.0'}
+
'@babel/code-frame@7.10.4':
resolution: {integrity: sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==}
@@ -12341,6 +12414,10 @@ packages:
stickyfill@1.1.1:
resolution: {integrity: sha512-GCp7vHAfpao+Qh/3Flh9DXEJ/qSi0KJwJw6zYlZOtRYXWUIpMM6mC2rIep/dK8RQqwW0KxGJIllmjPIBOGN8AA==}
+ stoppable@1.1.0:
+ resolution: {integrity: sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==}
+ engines: {node: '>=4', npm: '>=6'}
+
storybook@8.4.7:
resolution: {integrity: sha512-RP/nMJxiWyFc8EVMH5gp20ID032Wvk+Yr3lmKidoegto5Iy+2dVQnUoElZb2zpbVXNHWakGuAkfI0dY1Hfp/vw==}
hasBin: true
@@ -14401,6 +14478,138 @@ snapshots:
'@smithy/types': 3.7.2
tslib: 2.8.1
+ '@azure/abort-controller@2.1.2':
+ dependencies:
+ tslib: 2.8.1
+
+ '@azure/core-auth@1.9.0':
+ dependencies:
+ '@azure/abort-controller': 2.1.2
+ '@azure/core-util': 1.11.0
+ tslib: 2.8.1
+
+ '@azure/core-client@1.9.2':
+ dependencies:
+ '@azure/abort-controller': 2.1.2
+ '@azure/core-auth': 1.9.0
+ '@azure/core-rest-pipeline': 1.18.2
+ '@azure/core-tracing': 1.2.0
+ '@azure/core-util': 1.11.0
+ '@azure/logger': 1.1.4
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@azure/core-http-compat@2.1.2':
+ dependencies:
+ '@azure/abort-controller': 2.1.2
+ '@azure/core-client': 1.9.2
+ '@azure/core-rest-pipeline': 1.18.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@azure/core-lro@2.7.2':
+ dependencies:
+ '@azure/abort-controller': 2.1.2
+ '@azure/core-util': 1.11.0
+ '@azure/logger': 1.1.4
+ tslib: 2.8.1
+
+ '@azure/core-paging@1.6.2':
+ dependencies:
+ tslib: 2.8.1
+
+ '@azure/core-rest-pipeline@1.18.2':
+ dependencies:
+ '@azure/abort-controller': 2.1.2
+ '@azure/core-auth': 1.9.0
+ '@azure/core-tracing': 1.2.0
+ '@azure/core-util': 1.11.0
+ '@azure/logger': 1.1.4
+ http-proxy-agent: 7.0.2
+ https-proxy-agent: 7.0.6
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@azure/core-tracing@1.2.0':
+ dependencies:
+ tslib: 2.8.1
+
+ '@azure/core-util@1.11.0':
+ dependencies:
+ '@azure/abort-controller': 2.1.2
+ tslib: 2.8.1
+
+ '@azure/core-xml@1.4.4':
+ dependencies:
+ fast-xml-parser: 4.5.1
+ tslib: 2.8.1
+
+ '@azure/identity@4.5.0':
+ dependencies:
+ '@azure/abort-controller': 2.1.2
+ '@azure/core-auth': 1.9.0
+ '@azure/core-client': 1.9.2
+ '@azure/core-rest-pipeline': 1.18.2
+ '@azure/core-tracing': 1.2.0
+ '@azure/core-util': 1.11.0
+ '@azure/logger': 1.1.4
+ '@azure/msal-browser': 3.28.0
+ '@azure/msal-node': 2.16.2
+ events: 3.3.0
+ jws: 4.0.0
+ open: 8.4.2
+ stoppable: 1.1.0
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@azure/logger@1.1.4':
+ dependencies:
+ tslib: 2.8.1
+
+ '@azure/microsoft-playwright-testing@1.0.0-beta.6(@playwright/test@1.49.1)':
+ dependencies:
+ '@azure/core-rest-pipeline': 1.18.2
+ '@azure/identity': 4.5.0
+ '@azure/logger': 1.1.4
+ '@azure/storage-blob': 12.26.0
+ '@playwright/test': 1.49.1
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@azure/msal-browser@3.28.0':
+ dependencies:
+ '@azure/msal-common': 14.16.0
+
+ '@azure/msal-common@14.16.0': {}
+
+ '@azure/msal-node@2.16.2':
+ dependencies:
+ '@azure/msal-common': 14.16.0
+ jsonwebtoken: 9.0.2
+ uuid: 8.3.2
+
+ '@azure/storage-blob@12.26.0':
+ dependencies:
+ '@azure/abort-controller': 2.1.2
+ '@azure/core-auth': 1.9.0
+ '@azure/core-client': 1.9.2
+ '@azure/core-http-compat': 2.1.2
+ '@azure/core-lro': 2.7.2
+ '@azure/core-paging': 1.6.2
+ '@azure/core-rest-pipeline': 1.18.2
+ '@azure/core-tracing': 1.2.0
+ '@azure/core-util': 1.11.0
+ '@azure/core-xml': 1.4.4
+ '@azure/logger': 1.1.4
+ events: 3.3.0
+ tslib: 2.8.1
+ transitivePeerDependencies:
+ - supports-color
+
'@babel/code-frame@7.10.4':
dependencies:
'@babel/highlight': 7.25.9
@@ -28001,6 +28210,8 @@ snapshots:
stickyfill@1.1.1: {}
+ stoppable@1.1.0: {}
+
storybook@8.4.7(prettier@3.4.2):
dependencies:
'@storybook/core': 8.4.7(prettier@3.4.2)