import { render } from '@testing-library/react';
import { describe, it, expect } from 'vitest';
import { IsotypeFrames } from './IsotypeFrames';
import { ISOTYPE_FRAME_VARIANTS, ISOTYPE_FRAME_VARIANT_IDS } from './frame-variants';
import { ISOTYPE_COLORS } from './patterns';
import React from 'react';
describe('IsotypeFrames', () => {
it('renders two overlapping rects', () => {
const { container } = render();
expect(container.querySelectorAll('rect')).toHaveLength(2);
});
it('defaults to the blue-magenta variant', () => {
const { container } = render();
const rects = container.querySelectorAll('rect');
const [backKey, frontKey] = ISOTYPE_FRAME_VARIANTS['blue-magenta'];
expect(rects[0].getAttribute('stroke')).toBe(ISOTYPE_COLORS[backKey]);
expect(rects[1].getAttribute('stroke')).toBe(ISOTYPE_COLORS[frontKey]);
});
it('renders every registered variant with the right stroke colors', () => {
for (const variant of ISOTYPE_FRAME_VARIANT_IDS) {
const { container } = render();
const rects = container.querySelectorAll('rect');
const [backKey, frontKey] = ISOTYPE_FRAME_VARIANTS[variant];
expect(rects[0].getAttribute('stroke')).toBe(ISOTYPE_COLORS[backKey]);
expect(rects[1].getAttribute('stroke')).toBe(ISOTYPE_COLORS[frontKey]);
}
});
it('renders each rect hollow (fill="none")', () => {
const { container } = render();
container.querySelectorAll('rect').forEach(rect => {
expect(rect.getAttribute('fill')).toBe('none');
});
});
it('passes className through to the root', () => {
const { container } = render();
expect((container.firstChild as HTMLElement).className).toContain('w-64');
});
it('offsets the front frame diagonally from the back frame by a sixth of the shared size', () => {
const { container } = render();
const [back, front] = Array.from(container.querySelectorAll('rect'));
const backX = Number(back.getAttribute('x'));
const backY = Number(back.getAttribute('y'));
const backSize = Number(back.getAttribute('width'));
const frontX = Number(front.getAttribute('x'));
const frontY = Number(front.getAttribute('y'));
const frontSize = Number(front.getAttribute('width'));
expect(backSize).toBe(frontSize);
expect(frontX - backX).toBeCloseTo(backSize / 6);
expect(frontY - backY).toBeCloseTo(backSize / 6);
});
it('renders both frames fully opaque and blends their intersection via mix-blend-mode on the front frame', () => {
const { container } = render();
const [back, front] = Array.from(container.querySelectorAll('rect'));
expect(back.getAttribute('opacity')).toBeNull();
expect(front.getAttribute('opacity')).toBeNull();
expect(front.style.mixBlendMode).toBeTruthy();
expect(front.style.mixBlendMode).not.toBe('normal');
});
it('isolates the blend mode to the two frames via the root isolate class', () => {
const { container } = render();
expect((container.firstChild as HTMLElement).className).toContain('isolate');
});
it('expands the viewBox so no edge of the stroked frames is clipped', () => {
const { container } = render();
const svg = container.querySelector('svg')!;
const [minX, minY, width, height] = svg.getAttribute('viewBox')!.split(' ').map(Number);
container.querySelectorAll('rect').forEach(rect => {
const x = Number(rect.getAttribute('x'));
const y = Number(rect.getAttribute('y'));
const size = Number(rect.getAttribute('width'));
const half = Number(rect.getAttribute('stroke-width')) / 2;
expect(x - half).toBeGreaterThanOrEqual(minX);
expect(y - half).toBeGreaterThanOrEqual(minY);
expect(x + size + half).toBeLessThanOrEqual(minX + width);
expect(y + size + half).toBeLessThanOrEqual(minY + height);
});
});
});