test: verify embed helpers (#11318)
* test: add test cases to `window.getEmbedNamespace` * test: add test cases when window.name doesn't have cal-embed= * test: add test case to `getembedTheme` returning the theme from embedStore * test: add test to `getEmbedTheme` returning the theme from query params * Small refactor to reduce the time taken by tests and fix missing typings for embed functions on window * Fix weird test failure * Fix type import error caught by api.test.test --------- Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { EmbedThemeConfig } from "./embed-iframe";
|
||||
import type { EmbedThemeConfig } from "./src/types";
|
||||
|
||||
export default function EmbedInitIframe() {
|
||||
if (typeof window === "undefined" || window.isEmbed) {
|
||||
|
||||
@@ -2,6 +2,8 @@ import { expect } from "@playwright/test";
|
||||
|
||||
import { test } from "@calcom/web/playwright/lib/fixtures";
|
||||
|
||||
import "../../src/types";
|
||||
|
||||
test.describe("Embed Pages", () => {
|
||||
test("Event Type Page: should not have margin top on embed page", async ({ page }) => {
|
||||
await page.goto("http://localhost:3000/free/30min/embed");
|
||||
@@ -45,18 +47,77 @@ test.describe("Embed Pages", () => {
|
||||
expect(marginFromTop).not.toBe(0);
|
||||
});
|
||||
|
||||
test("should change to embed when window.name is changed to cal-embed=", async ({ page }) => {
|
||||
await page.goto("http://localhost:3000/free/30min");
|
||||
test.describe("isEmbed, getEmbedNamespace, getEmbedTheme testing", () => {
|
||||
test("when `window.name` is set to 'cal-embed=' and `theme` is supplied as a query param", async ({
|
||||
page,
|
||||
}) => {
|
||||
const queryParamTheme = "dark";
|
||||
await page.evaluate(() => {
|
||||
window.name = "cal-embed=";
|
||||
});
|
||||
|
||||
await page.evaluate(() => {
|
||||
window.name = "cal-embed=";
|
||||
await page.goto(`http://localhost:3000/free/30min?theme=${queryParamTheme}`);
|
||||
|
||||
const isEmbed = await page.evaluate(() => {
|
||||
return window?.isEmbed?.();
|
||||
});
|
||||
|
||||
const embedNamespace = await page.evaluate(() => {
|
||||
return window?.getEmbedNamespace?.();
|
||||
});
|
||||
|
||||
expect(embedNamespace).toBe("");
|
||||
expect(isEmbed).toBe(true);
|
||||
const embedTheme = await page.evaluate(() => {
|
||||
return window?.getEmbedTheme?.();
|
||||
});
|
||||
expect(embedTheme).toBe(queryParamTheme);
|
||||
const embedStoreTheme = await page.evaluate(() => {
|
||||
return window.CalEmbed.embedStore.theme;
|
||||
});
|
||||
// Verify that the theme is set on embedStore.
|
||||
expect(embedStoreTheme).toBe(queryParamTheme);
|
||||
});
|
||||
|
||||
await page.reload();
|
||||
|
||||
const isEmbed = await page.evaluate(() => {
|
||||
return window?.isEmbed?.();
|
||||
test("when `window.name` does not contain `cal-embed=`", async ({ page }) => {
|
||||
await page.evaluate(() => {
|
||||
window.name = "testing";
|
||||
});
|
||||
await page.goto(`http://localhost:3000/free/30min`);
|
||||
const isEmbed = await page.evaluate(() => {
|
||||
return window?.isEmbed?.();
|
||||
});
|
||||
const embedNamespace = await page.evaluate(() => {
|
||||
return window?.getEmbedNamespace?.();
|
||||
});
|
||||
expect(isEmbed).toBe(false);
|
||||
expect(embedNamespace).toBe(null);
|
||||
});
|
||||
|
||||
test("`getEmbedTheme` should use `window.CalEmbed.embedStore.theme` instead of `theme` query param if set", async ({
|
||||
page,
|
||||
}) => {
|
||||
const theme = "dark";
|
||||
await page.evaluate(() => {
|
||||
window.name = "cal-embed=";
|
||||
});
|
||||
await page.goto("http://localhost:3000/free/30min?theme=dark");
|
||||
let embedTheme = await page.evaluate(() => {
|
||||
return window?.getEmbedTheme?.();
|
||||
});
|
||||
expect(embedTheme).toBe(theme);
|
||||
|
||||
// Fake a scenario where theme query param is lost during navigation
|
||||
await page.evaluate(() => {
|
||||
history.pushState({}, "", "/free/30min");
|
||||
});
|
||||
|
||||
embedTheme = await page.evaluate(() => {
|
||||
return window?.getEmbedTheme?.();
|
||||
});
|
||||
|
||||
// Theme should still remain same as it's read from `window.CalEmbed.embedStore.theme` which is updated by getEmbedTheme itself
|
||||
expect(embedTheme).toBe(theme);
|
||||
});
|
||||
expect(isEmbed).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,29 +1,22 @@
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import type { CSSProperties } from "react";
|
||||
import { useEffect, useRef, useState, useCallback } from "react";
|
||||
|
||||
import type { Message } from "./embed";
|
||||
import { sdkActionManager } from "./sdk-event";
|
||||
|
||||
type Theme = "dark" | "light";
|
||||
|
||||
export type BookerLayouts = "month_view" | "week_view" | "column_view";
|
||||
|
||||
export type EmbedThemeConfig = Theme | "auto";
|
||||
export type UiConfig = {
|
||||
hideEventTypeDetails?: boolean;
|
||||
// If theme not provided we would get null
|
||||
theme?: EmbedThemeConfig | null;
|
||||
styles?: EmbedStyles & EmbedNonStylesConfig;
|
||||
//TODO: Extract from tailwind the list of all custom variables and support them in auto-completion as well as runtime validation. Followup with listing all variables in Embed Snippet Generator UI.
|
||||
cssVarsPerTheme?: Record<Theme, Record<string, string>>;
|
||||
layout?: BookerLayouts;
|
||||
colorScheme?: string | null;
|
||||
};
|
||||
import type { EmbedThemeConfig, UiConfig, EmbedNonStylesConfig, BookerLayouts, EmbedStyles } from "./types";
|
||||
|
||||
type SetStyles = React.Dispatch<React.SetStateAction<EmbedStyles>>;
|
||||
type setNonStylesConfig = React.Dispatch<React.SetStateAction<EmbedNonStylesConfig>>;
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
CalEmbed: {
|
||||
__logQueue?: unknown[];
|
||||
embedStore: typeof embedStore;
|
||||
applyCssVars: (cssVarsPerTheme: UiConfig["cssVarsPerTheme"]) => void;
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* This is in-memory persistence needed so that when user browses through the embed, the configurations from the instructions aren't lost.
|
||||
*/
|
||||
@@ -47,20 +40,6 @@ const embedStore = {
|
||||
setUiConfig: [] as ((arg0: UiConfig) => void)[],
|
||||
};
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
CalEmbed: {
|
||||
__logQueue?: unknown[];
|
||||
embedStore: typeof embedStore;
|
||||
applyCssVars: (cssVarsPerTheme: UiConfig["cssVarsPerTheme"]) => void;
|
||||
};
|
||||
CalComPageStatus: string;
|
||||
isEmbed?: () => boolean;
|
||||
getEmbedNamespace: () => string | null;
|
||||
getEmbedTheme: () => EmbedThemeConfig | null;
|
||||
}
|
||||
}
|
||||
|
||||
let isSafariBrowser = false;
|
||||
const isBrowser = typeof window !== "undefined";
|
||||
|
||||
@@ -100,23 +79,6 @@ function log(...args: unknown[]) {
|
||||
}
|
||||
}
|
||||
|
||||
// Only allow certain styles to be modified so that when we make any changes to HTML, we know what all embed styles might be impacted.
|
||||
// Keep this list to minimum, only adding those styles which are really needed.
|
||||
interface EmbedStyles {
|
||||
body?: Pick<CSSProperties, "background">;
|
||||
eventTypeListItem?: Pick<CSSProperties, "background" | "color" | "backgroundColor">;
|
||||
enabledDateButton?: Pick<CSSProperties, "background" | "color" | "backgroundColor">;
|
||||
disabledDateButton?: Pick<CSSProperties, "background" | "color" | "backgroundColor">;
|
||||
availabilityDatePicker?: Pick<CSSProperties, "background" | "color" | "backgroundColor">;
|
||||
}
|
||||
interface EmbedNonStylesConfig {
|
||||
/** Default would be center */
|
||||
align?: "left";
|
||||
branding?: {
|
||||
brandColor?: string;
|
||||
};
|
||||
}
|
||||
|
||||
const setEmbedStyles = (stylesConfig: EmbedStyles) => {
|
||||
embedStore.styles = stylesConfig;
|
||||
for (const [, setEmbedStyle] of Object.entries(embedStore.reactStylesStateSetters)) {
|
||||
|
||||
@@ -2,17 +2,12 @@
|
||||
import { FloatingButton } from "./FloatingButton/FloatingButton";
|
||||
import { Inline } from "./Inline/inline";
|
||||
import { ModalBox } from "./ModalBox/ModalBox";
|
||||
import type {
|
||||
InterfaceWithParent,
|
||||
interfaceWithParent,
|
||||
UiConfig,
|
||||
EmbedThemeConfig,
|
||||
BookerLayouts,
|
||||
} from "./embed-iframe";
|
||||
import type { InterfaceWithParent, interfaceWithParent } from "./embed-iframe";
|
||||
import css from "./embed.css";
|
||||
import { SdkActionManager } from "./sdk-action-manager";
|
||||
import type { EventData, EventDataMap } from "./sdk-action-manager";
|
||||
import allCss from "./tailwind.generated.css?inline";
|
||||
import type { UiConfig, EmbedThemeConfig, BookerLayouts } from "./types";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type Rest<T extends any[]> = T extends [any, ...infer U] ? U : never;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
import type { CSSProperties } from "react";
|
||||
|
||||
type Theme = "dark" | "light";
|
||||
export type EmbedThemeConfig = Theme | "auto";
|
||||
|
||||
export type BookerLayouts = "month_view" | "week_view" | "column_view";
|
||||
// Only allow certain styles to be modified so that when we make any changes to HTML, we know what all embed styles might be impacted.
|
||||
// Keep this list to minimum, only adding those styles which are really needed.
|
||||
export interface EmbedStyles {
|
||||
body?: Pick<CSSProperties, "background">;
|
||||
eventTypeListItem?: Pick<CSSProperties, "background" | "color" | "backgroundColor">;
|
||||
enabledDateButton?: Pick<CSSProperties, "background" | "color" | "backgroundColor">;
|
||||
disabledDateButton?: Pick<CSSProperties, "background" | "color" | "backgroundColor">;
|
||||
availabilityDatePicker?: Pick<CSSProperties, "background" | "color" | "backgroundColor">;
|
||||
}
|
||||
|
||||
export interface EmbedNonStylesConfig {
|
||||
/** Default would be center */
|
||||
align?: "left";
|
||||
branding?: {
|
||||
brandColor?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type UiConfig = {
|
||||
hideEventTypeDetails?: boolean;
|
||||
// If theme not provided we would get null
|
||||
theme?: EmbedThemeConfig | null;
|
||||
styles?: EmbedStyles & EmbedNonStylesConfig;
|
||||
//TODO: Extract from tailwind the list of all custom variables and support them in auto-completion as well as runtime validation. Followup with listing all variables in Embed Snippet Generator UI.
|
||||
cssVarsPerTheme?: Record<Theme, Record<string, string>>;
|
||||
layout?: BookerLayouts;
|
||||
colorScheme?: string | null;
|
||||
};
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
CalComPageStatus: string;
|
||||
isEmbed?: () => boolean;
|
||||
getEmbedNamespace: () => string | null;
|
||||
getEmbedTheme: () => EmbedThemeConfig | null;
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
@@ -14,9 +14,10 @@ const api = getCalApi();
|
||||
test("Check that the API is available", async () => {
|
||||
expect(api).toBeDefined()
|
||||
const awaitedApi = await api;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
awaitedApi('floatingButton', {
|
||||
calLink: 'free',
|
||||
config: {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-expect-error We are intentionaly testing invalid value
|
||||
layout: 'wrongview'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user