feat(config): parse data-shadow attribute on script tag
Adds the boolean field consumed by the upcoming Shadow DOM mount path. Strict equality with the string "true" — anything else (absent, "false", "1", "yes", empty) yields false, so accidental opt-in is impossible. Tests cover the full parseConfig surface (required fields, server-url derivation, position default) plus the new shadow attribute parsing. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ca084735ac
commit
8d0696584c
@ -3,6 +3,7 @@ export type WidgetConfig = {
|
||||
apiKey: string;
|
||||
serverUrl: string;
|
||||
position?: 'bottom-right' | 'bottom-left';
|
||||
shadow: boolean;
|
||||
};
|
||||
|
||||
export function parseConfig(): WidgetConfig | null {
|
||||
@ -22,10 +23,11 @@ export function parseConfig(): WidgetConfig | null {
|
||||
| 'bottom-right'
|
||||
| 'bottom-left'
|
||||
| null) ?? 'bottom-right';
|
||||
const shadow = script.getAttribute('data-shadow') === 'true';
|
||||
|
||||
if (!botId || !apiKey) return null;
|
||||
|
||||
return { botId, apiKey, serverUrl, position };
|
||||
return { botId, apiKey, serverUrl, position, shadow };
|
||||
}
|
||||
|
||||
function deriveServerFromSrc(src: string): string {
|
||||
|
||||
118
tests/unit/config.test.ts
Normal file
118
tests/unit/config.test.ts
Normal file
@ -0,0 +1,118 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { parseConfig } from '@/config';
|
||||
|
||||
function createScript(
|
||||
attrs: Record<string, string>,
|
||||
src = 'https://cdn.example.com/messenzy-widget.iife.js',
|
||||
): HTMLScriptElement {
|
||||
const script = document.createElement('script');
|
||||
script.src = src;
|
||||
for (const [key, value] of Object.entries(attrs)) {
|
||||
script.setAttribute(key, value);
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
return script;
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
document.head.innerHTML = '';
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
describe('parseConfig', () => {
|
||||
it('returns null when no script with data-bot-id is present', () => {
|
||||
expect(parseConfig()).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null when data-api-key is missing', () => {
|
||||
createScript({ 'data-bot-id': 'b1' });
|
||||
expect(parseConfig()).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null when data-bot-id is missing', () => {
|
||||
createScript({ 'data-api-key': 'k1' });
|
||||
expect(parseConfig()).toBeNull();
|
||||
});
|
||||
|
||||
it('parses required fields', () => {
|
||||
createScript({ 'data-bot-id': 'b1', 'data-api-key': 'k1' });
|
||||
const config = parseConfig();
|
||||
expect(config).not.toBeNull();
|
||||
expect(config?.botId).toBe('b1');
|
||||
expect(config?.apiKey).toBe('k1');
|
||||
});
|
||||
|
||||
it('derives serverUrl from script src when data-server-url absent', () => {
|
||||
createScript(
|
||||
{ 'data-bot-id': 'b1', 'data-api-key': 'k1' },
|
||||
'https://widgets.example.com/path/messenzy.iife.js',
|
||||
);
|
||||
expect(parseConfig()?.serverUrl).toBe('https://widgets.example.com');
|
||||
});
|
||||
|
||||
it('respects explicit data-server-url override', () => {
|
||||
createScript({
|
||||
'data-bot-id': 'b1',
|
||||
'data-api-key': 'k1',
|
||||
'data-server-url': 'https://api.foo.test',
|
||||
});
|
||||
expect(parseConfig()?.serverUrl).toBe('https://api.foo.test');
|
||||
});
|
||||
|
||||
it('defaults position to bottom-right', () => {
|
||||
createScript({ 'data-bot-id': 'b1', 'data-api-key': 'k1' });
|
||||
expect(parseConfig()?.position).toBe('bottom-right');
|
||||
});
|
||||
|
||||
it('parses position bottom-left', () => {
|
||||
createScript({
|
||||
'data-bot-id': 'b1',
|
||||
'data-api-key': 'k1',
|
||||
'data-position': 'bottom-left',
|
||||
});
|
||||
expect(parseConfig()?.position).toBe('bottom-left');
|
||||
});
|
||||
|
||||
describe('shadow attribute (PR B opt-in)', () => {
|
||||
it('shadow=false when data-shadow absent', () => {
|
||||
createScript({ 'data-bot-id': 'b1', 'data-api-key': 'k1' });
|
||||
expect(parseConfig()?.shadow).toBe(false);
|
||||
});
|
||||
|
||||
it('shadow=true when data-shadow="true"', () => {
|
||||
createScript({
|
||||
'data-bot-id': 'b1',
|
||||
'data-api-key': 'k1',
|
||||
'data-shadow': 'true',
|
||||
});
|
||||
expect(parseConfig()?.shadow).toBe(true);
|
||||
});
|
||||
|
||||
it('shadow=false when data-shadow="false"', () => {
|
||||
createScript({
|
||||
'data-bot-id': 'b1',
|
||||
'data-api-key': 'k1',
|
||||
'data-shadow': 'false',
|
||||
});
|
||||
expect(parseConfig()?.shadow).toBe(false);
|
||||
});
|
||||
|
||||
it('shadow=false when data-shadow="" (empty)', () => {
|
||||
createScript({
|
||||
'data-bot-id': 'b1',
|
||||
'data-api-key': 'k1',
|
||||
'data-shadow': '',
|
||||
});
|
||||
expect(parseConfig()?.shadow).toBe(false);
|
||||
});
|
||||
|
||||
it('shadow=false for unrecognized values (e.g. "1", "yes")', () => {
|
||||
createScript({
|
||||
'data-bot-id': 'b1',
|
||||
'data-api-key': 'k1',
|
||||
'data-shadow': '1',
|
||||
});
|
||||
expect(parseConfig()?.shadow).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user