From 037499a65bedbf27741bda5dc94a45488b62f7b5 Mon Sep 17 00:00:00 2001 From: tom Date: Tue, 30 Jan 2024 19:53:33 +0400 Subject: [PATCH] [skip ci] testing feature flags --- .env.example | 3 ++- playwright-ct.config.ts | 3 +++ playwright/fixtures/contextWithEnvs.ts | 16 ++---------- playwright/fixtures/contextWithFeatures.ts | 18 +++++++++++++ .../fixtures/createContextWithStorage.ts | 14 +++++++++++ .../mocks/lib/growthbook/useFeatureValue.js | 25 +++++++++++++++++++ ui/pages/Login.pw.tsx | 25 +++++++++++++++++++ ui/snippets/header/Burger.pw.tsx | 5 ++-- .../navigation/NavigationDesktop.pw.tsx | 9 ++++--- 9 files changed, 97 insertions(+), 21 deletions(-) create mode 100644 playwright/fixtures/contextWithFeatures.ts create mode 100644 playwright/fixtures/createContextWithStorage.ts create mode 100644 playwright/mocks/lib/growthbook/useFeatureValue.js create mode 100644 ui/pages/Login.pw.tsx diff --git a/.env.example b/.env.example index 0b7354a1fc..4bdc5535b1 100644 --- a/.env.example +++ b/.env.example @@ -6,4 +6,5 @@ NEXT_PUBLIC_GOOGLE_ANALYTICS_PROPERTY_ID=UA-XXXXXX-X NEXT_PUBLIC_MIXPANEL_PROJECT_TOKEN=xxx NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY=xxx NEXT_PUBLIC_AUTH0_CLIENT_ID=xxx -FAVICON_GENERATOR_API_KEY=xxx \ No newline at end of file +FAVICON_GENERATOR_API_KEY=xxx +NEXT_PUBLIC_GROWTH_BOOK_CLIENT_KEY=xxx \ No newline at end of file diff --git a/playwright-ct.config.ts b/playwright-ct.config.ts index eccae16aa5..3ca605249d 100644 --- a/playwright-ct.config.ts +++ b/playwright-ct.config.ts @@ -65,6 +65,9 @@ const config: PlaywrightTestConfig = defineConfig({ // so for now we just mock these modules in tests '@metamask/post-message-stream': './playwright/mocks/modules/@metamask/post-message-stream.js', '@metamask/providers': './playwright/mocks/modules/@metamask/providers.js', + + // Mock for growthbook to test feature flags + 'lib/growthbook/useFeatureValue': './playwright/mocks/lib/growthbook/useFeatureValue.js', }, }, define: { diff --git a/playwright/fixtures/contextWithEnvs.ts b/playwright/fixtures/contextWithEnvs.ts index 5281646245..e703a922fb 100644 --- a/playwright/fixtures/contextWithEnvs.ts +++ b/playwright/fixtures/contextWithEnvs.ts @@ -1,7 +1,6 @@ import type { test } from '@playwright/experimental-ct-react'; -import type { Browser } from '@playwright/test'; -import * as app from 'playwright/utils/app'; +import createContextWithStorage from './createContextWithStorage'; interface Env { name: string; @@ -11,20 +10,9 @@ interface Env { // keep in mind that all passed variables here should be present in env config files (.env.pw or .env.poa) export default function contextWithEnvsFixture(envs: Array): Parameters[0]['context'] { return async({ browser }, use) => { - const context = await createContextWithEnvs(browser, envs); + const context = await createContextWithStorage(browser, envs); await use(context); await context.close(); }; } - -export async function createContextWithEnvs(browser: Browser, envs: Array) { - return browser.newContext({ - storageState: { - origins: [ - { origin: app.url, localStorage: envs }, - ], - cookies: [], - }, - }); -} diff --git a/playwright/fixtures/contextWithFeatures.ts b/playwright/fixtures/contextWithFeatures.ts new file mode 100644 index 0000000000..af12c77d34 --- /dev/null +++ b/playwright/fixtures/contextWithFeatures.ts @@ -0,0 +1,18 @@ +import type { test } from '@playwright/experimental-ct-react'; + +import createContextWithStorage from './createContextWithStorage'; + +interface Feature { + id: string; + value: unknown; +} + +export default function contextWithFeaturesFixture(envs: Array): Parameters[0]['context'] { + return async({ browser }, use) => { + const storageItems = envs.map(({ id, value }) => ({ name: `pw_feature:${ id }`, value: JSON.stringify({ value, type: typeof value }) })); + const context = await createContextWithStorage(browser, storageItems); + + await use(context); + await context.close(); + }; +} diff --git a/playwright/fixtures/createContextWithStorage.ts b/playwright/fixtures/createContextWithStorage.ts new file mode 100644 index 0000000000..f2555b37c0 --- /dev/null +++ b/playwright/fixtures/createContextWithStorage.ts @@ -0,0 +1,14 @@ +import type { Browser } from '@playwright/test'; + +import * as app from 'playwright/utils/app'; + +export default async function createContextWithEnvs(browser: Browser, localStorage: Array<{ name: string; value: string }>) { + return browser.newContext({ + storageState: { + origins: [ + { origin: app.url, localStorage }, + ], + cookies: [], + }, + }); +} diff --git a/playwright/mocks/lib/growthbook/useFeatureValue.js b/playwright/mocks/lib/growthbook/useFeatureValue.js new file mode 100644 index 0000000000..c7abf1a49e --- /dev/null +++ b/playwright/mocks/lib/growthbook/useFeatureValue.js @@ -0,0 +1,25 @@ +const useFeatureValue = (name, fallback) => { + try { + const { value, type } = JSON.parse(localStorage.getItem(`pw_feature:${ name }`)); + + const formattedValue = (() => { + switch (type) { + case 'boolean': { + return value === 'true'; + } + case 'number': { + return Number(value); + } + default: + return value; + } + })(); + + return { isLoading: false, value: formattedValue }; + + } catch (error) { + return { isLoading: false, value: fallback }; + } +}; + +export default useFeatureValue; diff --git a/ui/pages/Login.pw.tsx b/ui/pages/Login.pw.tsx new file mode 100644 index 0000000000..5a56a8b55c --- /dev/null +++ b/ui/pages/Login.pw.tsx @@ -0,0 +1,25 @@ +import { test as base, expect } from '@playwright/experimental-ct-react'; +import React from 'react'; + +import contextWithFeatures from 'playwright/fixtures/contextWithFeatures'; +import TestApp from 'playwright/TestApp'; + +import Login from './Login'; + +const testWithFeature = base.extend({ + context: contextWithFeatures([ + { id: 'test_value', value: 'kitty' }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ]) as any, +}); + +testWithFeature('has feature text', async({ mount }) => { + const component = await mount( + + + , + ); + + const featureText = component.getByText('kitty'); + await expect(featureText).toBeVisible(); +}); diff --git a/ui/snippets/header/Burger.pw.tsx b/ui/snippets/header/Burger.pw.tsx index 4fc912b6de..9f64412b9b 100644 --- a/ui/snippets/header/Burger.pw.tsx +++ b/ui/snippets/header/Burger.pw.tsx @@ -4,7 +4,8 @@ import React from 'react'; import { buildExternalAssetFilePath } from 'configs/app/utils'; import { FEATURED_NETWORKS_MOCK } from 'mocks/config/network'; import authFixture from 'playwright/fixtures/auth'; -import contextWithEnvs, { createContextWithEnvs } from 'playwright/fixtures/contextWithEnvs'; +import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; +import createContextWithStorage from 'playwright/fixtures/createContextWithStorage'; import TestApp from 'playwright/TestApp'; import * as app from 'playwright/utils/app'; @@ -104,7 +105,7 @@ test('submenu', async({ mount, page }) => { test.describe('auth', () => { const extendedTest = base.extend({ context: async({ browser }, use) => { - const context = await createContextWithEnvs(browser, [ + const context = await createContextWithStorage(browser, [ { name: 'NEXT_PUBLIC_FEATURED_NETWORKS', value: FEATURED_NETWORKS_URL }, ]); authFixture(context); diff --git a/ui/snippets/navigation/NavigationDesktop.pw.tsx b/ui/snippets/navigation/NavigationDesktop.pw.tsx index b47d6b59cb..9ba7520405 100644 --- a/ui/snippets/navigation/NavigationDesktop.pw.tsx +++ b/ui/snippets/navigation/NavigationDesktop.pw.tsx @@ -6,7 +6,8 @@ import React from 'react'; import { buildExternalAssetFilePath } from 'configs/app/utils'; import * as cookies from 'lib/cookies'; import authFixture from 'playwright/fixtures/auth'; -import contextWithEnvs, { createContextWithEnvs } from 'playwright/fixtures/contextWithEnvs'; +import contextWithEnvs from 'playwright/fixtures/contextWithEnvs'; +import createContextWithStorage from 'playwright/fixtures/createContextWithStorage'; import TestApp from 'playwright/TestApp'; import * as app from 'playwright/utils/app'; import * as configs from 'playwright/utils/configs'; @@ -61,7 +62,7 @@ test.describe('no auth', () => { base.describe('auth', () => { const test = base.extend({ context: async({ browser }, use) => { - const context = await createContextWithEnvs(browser, [ + const context = await createContextWithStorage(browser, [ { name: 'NEXT_PUBLIC_FEATURED_NETWORKS', value: FEATURED_NETWORKS_URL }, ]); authFixture(context); @@ -150,7 +151,7 @@ test.describe('with submenu', () => { base.describe('cookie set to false', () => { const test = base.extend({ context: async({ browser }, use) => { - const context = await createContextWithEnvs(browser, [ + const context = await createContextWithStorage(browser, [ { name: 'NEXT_PUBLIC_FEATURED_NETWORKS', value: FEATURED_NETWORKS_URL }, ]); context.addCookies([ { name: cookies.NAMES.NAV_BAR_COLLAPSED, value: 'false', domain: app.domain, path: '/' } ]); @@ -190,7 +191,7 @@ base.describe('cookie set to false', () => { base.describe('cookie set to true', () => { const test = base.extend({ context: async({ browser }, use) => { - const context = await createContextWithEnvs(browser, [ + const context = await createContextWithStorage(browser, [ { name: 'NEXT_PUBLIC_FEATURED_NETWORKS', value: FEATURED_NETWORKS_URL }, ]); context.addCookies([ { name: cookies.NAMES.NAV_BAR_COLLAPSED, value: 'true', domain: 'localhost', path: '/' } ]);