feat: rediseño «El documento» en curso, contacto real y enlaces externos
Primitivas y contenido del rediseño (escala tipográfica, Revelar, MarcaSeccion, Seccion, Antetitulo, SimboloMarca, Resplandor, ClaridadHero, sección Proceso de ocho etapas, lib/content.ts reestructurado en seis secciones con alias para las viejas). Mecanismo y FormaTrabajar se retiran. Encima, lo de esta sesión: - recorrido: creeer vuelve a responder cada situación en vez de repetirla (el alias apuntaba respuesta a situacion); respuestas en plural. Créditos de las seis fotos de México. - contacto: WhatsApp, email (con mailto:) y LinkedIn reales; iconos de las redes en el cierre y el footer (IconoRed, trazos de Simple Icons, CC0). - enlaces: lo externo abre en otra pestaña con noopener y aviso para lectores de pantalla (lib/enlaces.ts); mailto y anclas se quedan. - Proceso: las ocho tarjetas no aparecían nunca en móvil (amount 0.3 sobre una lista de ~3000px); ahora cada una se revela sola. El ≡ de la 08 ya no tapa un entregable, título equilibrado, etiqueta a 12px, sin elevar al hover lo que no es clicable. - Disciplinas: la frase «No son la promesa — son los medios» gana peso y se agrupa con título e ilustración; sin cortes huérfanos en móvil. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3242a8bf71
commit
89e54a6e84
73 files changed
+5261
-592
No files matched your search
@@ -0,0 +1,60 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { Antetitulo } from '../Antetitulo';
|
||||
|
||||
describe('<Antetitulo />', () => {
|
||||
it('renders its copy', () => {
|
||||
render(<Antetitulo>el contexto</Antetitulo>);
|
||||
expect(screen.getByText('el contexto')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('sobre fondo oscuro usa el dorado del manual', () => {
|
||||
// Sobre azul profundo, #C4942E da 6,07:1: es donde el dorado de la marca
|
||||
// trabaja mejor y ahí se queda intacto.
|
||||
render(<Antetitulo sobre="oscuro">las herramientas</Antetitulo>);
|
||||
expect(screen.getByText('las herramientas').className.split(/\s+/)).toContain('text-dorado');
|
||||
});
|
||||
|
||||
it('sobre fondo claro baja al dorado legible', () => {
|
||||
// El mismo #C4942E cae a 2,76:1 sobre blanco y a 2,46:1 sobre gris-claro.
|
||||
// A 12px y en negrita eso no se lee: el antetítulo pasa a `dorado-texto`.
|
||||
render(<Antetitulo sobre="claro">lo que realmente sientes</Antetitulo>);
|
||||
const classes = screen.getByText('lo que realmente sientes').className.split(/\s+/);
|
||||
expect(classes).toContain('text-dorado-texto');
|
||||
expect(classes).not.toContain('text-dorado');
|
||||
});
|
||||
|
||||
it('asume fondo claro cuando no se le dice nada', () => {
|
||||
// El caso por defecto tiene que ser el seguro: olvidarse del prop no puede
|
||||
// producir un antetítulo ilegible.
|
||||
render(<Antetitulo>cómo empezar</Antetitulo>);
|
||||
expect(screen.getByText('cómo empezar').className.split(/\s+/)).toContain('text-dorado-texto');
|
||||
});
|
||||
|
||||
it('puede ser el encabezado de su sección', () => {
|
||||
// En el recorrido el antetítulo es el único <h2> que tiene la sección.
|
||||
render(<Antetitulo as="h2">¿Te reconoces?</Antetitulo>);
|
||||
expect(screen.getByRole('heading', { name: '¿Te reconoces?', level: 2 })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('lee su tamaño del token rotulo en vez de recomponerlo a mano', () => {
|
||||
// `rotulo` already declares 0.75rem with 0.3em of tracking. Repeating it
|
||||
// in the className left the token unused and the two definitions free
|
||||
// to drift apart.
|
||||
render(<Antetitulo>el contexto</Antetitulo>);
|
||||
const classes = screen.getByText('el contexto').className.split(/\s+/);
|
||||
expect(classes).toContain('text-rotulo');
|
||||
expect(classes).not.toContain('text-xs');
|
||||
expect(classes).not.toContain('tracking-[0.3em]');
|
||||
});
|
||||
|
||||
it('usa un peso que la tipografía tiene', () => {
|
||||
// Open Sans tops out at 800 and layout.tsx loads up to 800: `font-black`
|
||||
// (900) fell back to 800 anyway. The eyebrow goes at 600, the weight the
|
||||
// scale reserves for labels.
|
||||
render(<Antetitulo>el contexto</Antetitulo>);
|
||||
const classes = screen.getByText('el contexto').className.split(/\s+/);
|
||||
expect(classes).toContain('font-semibold');
|
||||
expect(classes).not.toContain('font-black');
|
||||
});
|
||||
});
|
||||
@@ -12,12 +12,22 @@ describe('<CTA />', () => {
|
||||
expect(screen.getByRole('link').getAttribute('href')).toBe('#contacto');
|
||||
});
|
||||
it('renders the ghost variant outlined, so it reads as secondary', () => {
|
||||
render(<CTA href="#mecanismo" label="Ver cómo trabajo" variant="ghost" />);
|
||||
render(<CTA href="#proceso" label="Ver cómo trabajamos" variant="ghost" />);
|
||||
const classes = screen.getByRole('link').className.split(/\s+/);
|
||||
expect(classes).toContain('border-azul-profundo');
|
||||
expect(classes).not.toContain('bg-azul-profundo'); // sólo se rellena en hover
|
||||
expect(classes).toContain('hover:bg-azul-profundo');
|
||||
});
|
||||
it('rellena en dorado con la etiqueta en azul profundo', () => {
|
||||
// Blanco sobre el dorado del manual (#C4942E) es 2,76:1 — no pasa AA para
|
||||
// texto pequeño, y esta es la variante del botón que cierra la página.
|
||||
// Azul profundo sobre ese mismo dorado da 6,07:1.
|
||||
render(<CTA href="#contacto" label="Define tu siguiente movimiento" variant="dark" />);
|
||||
const classes = screen.getByRole('link').className.split(/\s+/);
|
||||
expect(classes).toContain('bg-dorado');
|
||||
expect(classes).toContain('text-azul-profundo');
|
||||
expect(classes).not.toContain('text-white');
|
||||
});
|
||||
it('keeps every variant the same height', () => {
|
||||
const { container } = render(
|
||||
<>
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { ClaridadHero } from '../ClaridadHero';
|
||||
|
||||
describe('<ClaridadHero />', () => {
|
||||
it('is decorative: hidden from assistive tech', () => {
|
||||
const { container } = render(<ClaridadHero className="w-full h-full" />);
|
||||
expect(container.firstElementChild).toHaveAttribute('aria-hidden', 'true');
|
||||
});
|
||||
|
||||
it('holds the box open so nothing shifts when the scene arrives', () => {
|
||||
const { container } = render(<ClaridadHero className="w-full h-full" />);
|
||||
expect(container.firstElementChild).toHaveClass('w-full', 'h-full');
|
||||
});
|
||||
|
||||
// The stub observer in the test setup never reports an intersection, which is
|
||||
// exactly the case worth pinning: until the box comes near the viewport,
|
||||
// nothing mounts and three.js is never fetched.
|
||||
it('does not fetch three.js before the box comes near the viewport', () => {
|
||||
const { container } = render(<ClaridadHero className="w-full h-full" />);
|
||||
expect(container.firstElementChild).toBeEmptyDOMElement();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { describe, it, expect, vi, afterEach } from 'vitest';
|
||||
|
||||
// jsdom has no WebGL, so the real <Canvas> cannot mount. The mock keeps the
|
||||
// wrapper — the part that is plain DOM and that carries the decisions worth
|
||||
// protecting — testable, and reports back the frameloop it was handed.
|
||||
vi.mock('@react-three/fiber', () => ({
|
||||
Canvas: ({ frameloop }: { frameloop?: string }) => (
|
||||
<div data-testid="lienzo" data-frameloop={frameloop} />
|
||||
),
|
||||
useFrame: () => {},
|
||||
useThree: () => ({ camera: { position: { set: () => {} }, lookAt: () => {} }, invalidate: () => {} }),
|
||||
}));
|
||||
|
||||
import { EscenaClaridad } from '../EscenaClaridad';
|
||||
|
||||
function conMovimientoReducido(reducido: boolean) {
|
||||
window.matchMedia = ((query: string) => ({
|
||||
matches: reducido && query.includes('reduce'),
|
||||
media: query,
|
||||
onchange: null,
|
||||
addEventListener: () => {},
|
||||
removeEventListener: () => {},
|
||||
addListener: () => {},
|
||||
removeListener: () => {},
|
||||
dispatchEvent: () => false,
|
||||
})) as unknown as typeof window.matchMedia;
|
||||
}
|
||||
|
||||
afterEach(() => conMovimientoReducido(false));
|
||||
|
||||
describe('<EscenaClaridad />', () => {
|
||||
it('is decorative: hidden from assistive tech', () => {
|
||||
const { container } = render(<EscenaClaridad />);
|
||||
expect(container.firstElementChild).toHaveAttribute('aria-hidden', 'true');
|
||||
});
|
||||
|
||||
it('takes its size from the caller, so the hero reserves the box', () => {
|
||||
const { container } = render(<EscenaClaridad className="w-full h-full" />);
|
||||
expect(container.firstElementChild).toHaveClass('w-full', 'h-full');
|
||||
});
|
||||
|
||||
it('runs the loop while the hero is on screen', () => {
|
||||
conMovimientoReducido(false);
|
||||
const { getByTestId } = render(<EscenaClaridad />);
|
||||
expect(getByTestId('lienzo')).toHaveAttribute('data-frameloop', 'always');
|
||||
});
|
||||
|
||||
it('stops the loop for readers who asked for reduced motion', () => {
|
||||
conMovimientoReducido(true);
|
||||
const { getByTestId } = render(<EscenaClaridad />);
|
||||
expect(getByTestId('lienzo')).toHaveAttribute('data-frameloop', 'demand');
|
||||
});
|
||||
});
|
||||
@@ -1,18 +1,6 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { Icono, type IconoNombre } from '../Icono';
|
||||
|
||||
const NOMBRES: IconoNombre[] = [
|
||||
'inmersion',
|
||||
'desafiar',
|
||||
'estrategia',
|
||||
'escribeme',
|
||||
'diagnostico',
|
||||
'plan',
|
||||
'comunicacion',
|
||||
'digital',
|
||||
'experiencias',
|
||||
];
|
||||
import { Icono, NOMBRES_ICONO } from '../Icono';
|
||||
|
||||
describe('<Icono />', () => {
|
||||
it('is decorative: hidden from assistive tech', () => {
|
||||
@@ -20,7 +8,7 @@ describe('<Icono />', () => {
|
||||
expect(container.querySelector('svg')).toHaveAttribute('aria-hidden', 'true');
|
||||
});
|
||||
|
||||
it.each(NOMBRES)('renders a non-empty svg for "%s"', (nombre) => {
|
||||
it.each(NOMBRES_ICONO)('renders a non-empty svg for "%s"', (nombre) => {
|
||||
const { container } = render(<Icono nombre={nombre} />);
|
||||
const svg = container.querySelector('svg');
|
||||
expect(svg?.children.length).toBeGreaterThan(0);
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { IconoRed, NOMBRES_RED } from '../IconoRed';
|
||||
|
||||
describe('<IconoRed />', () => {
|
||||
it('is decorative: the link around it carries the name', () => {
|
||||
const { container } = render(<IconoRed nombre="whatsapp" />);
|
||||
expect(container.querySelector('svg')).toHaveAttribute('aria-hidden', 'true');
|
||||
});
|
||||
|
||||
it.each(NOMBRES_RED)('renders a non-empty svg for "%s"', (nombre) => {
|
||||
const { container } = render(<IconoRed nombre={nombre} />);
|
||||
expect(container.querySelector('svg')?.children.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('never brings its own brand colour onto the page', () => {
|
||||
for (const nombre of NOMBRES_RED) {
|
||||
const { container } = render(<IconoRed nombre={nombre} />);
|
||||
const pintados = container.querySelectorAll('[fill]:not([fill="none"]), [stroke]');
|
||||
pintados.forEach((el) => {
|
||||
const color = el.getAttribute('fill') ?? el.getAttribute('stroke');
|
||||
if (color !== 'none') expect(color).toBe('currentColor');
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,55 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { MarcaSeccion, PASOS } from '../MarcaSeccion';
|
||||
import { LINEAS_SIMBOLO } from '@/lib/simbolo';
|
||||
|
||||
const lineas = (paso: number) => {
|
||||
const { container } = render(<MarcaSeccion paso={paso} />);
|
||||
return Array.from(container.querySelectorAll('line'));
|
||||
};
|
||||
|
||||
const visibles = (paso: number) => lineas(paso).filter((l) => l.getAttribute('opacity') !== '0');
|
||||
|
||||
const coords = (paso: number) =>
|
||||
lineas(paso).map((l) =>
|
||||
(['x1', 'y1', 'x2', 'y2'] as const).map((a) => Number(l.getAttribute(a))),
|
||||
);
|
||||
|
||||
describe('<MarcaSeccion />', () => {
|
||||
it('opens the document with all nine lines tangled', () => {
|
||||
expect(visibles(0)).toHaveLength(9);
|
||||
});
|
||||
|
||||
it("resolves into the manual's ≡ exactly, not into a lookalike", () => {
|
||||
// Not a lookalike: the exact three bars that the wordmark draws.
|
||||
const finales = visibles(PASOS - 1);
|
||||
expect(finales).toHaveLength(3);
|
||||
finales.forEach((l, i) => {
|
||||
(['x1', 'y1', 'x2', 'y2'] as const).forEach((attr) => {
|
||||
expect(Number(l.getAttribute(attr)), attr).toBeCloseTo(LINEAS_SIMBOLO[i][attr], 5);
|
||||
});
|
||||
expect(Number(l.getAttribute('stroke-width'))).toBeCloseTo(LINEAS_SIMBOLO[i].grosor, 5);
|
||||
});
|
||||
});
|
||||
|
||||
it('converges step by step: each one leaves the lines closer to where they end up', () => {
|
||||
// Counting visible lines proves nothing here: the six that fade do it
|
||||
// continuously as `1 - t` and only reach a literal "0" at the last step,
|
||||
// so the count is [9,9,9,9,9,3] and the assertion cannot fail. What does
|
||||
// move at every step is how far each line still is from its final place.
|
||||
const destino = coords(PASOS - 1);
|
||||
const desvio = (paso: number) =>
|
||||
coords(paso).reduce(
|
||||
(total, linea, i) => total + Math.hypot(...linea.map((v, j) => v - destino[i][j])),
|
||||
0,
|
||||
);
|
||||
const serie = Array.from({ length: PASOS }, (_, p) => desvio(p));
|
||||
serie.slice(1).forEach((d, i) => expect(d, `paso ${i + 1}`).toBeLessThan(serie[i]));
|
||||
expect(serie[PASOS - 1]).toBe(0);
|
||||
});
|
||||
|
||||
it('is decorative for screen readers', () => {
|
||||
const { container } = render(<MarcaSeccion paso={2} />);
|
||||
expect(container.querySelector('svg')).toHaveAttribute('aria-hidden', 'true');
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,7 @@ const par = {
|
||||
n: 1,
|
||||
name: 'Crecer',
|
||||
situacion: 'El siguiente salto no tiene mapa.',
|
||||
respuesta: 'Me sumerjo hasta entender tu empresa desde dentro.',
|
||||
respuesta: 'Nos sumergimos hasta entender tu empresa desde dentro.',
|
||||
};
|
||||
|
||||
const montar = (extra: Partial<Parameters<typeof ParDialogo>[0]> = {}) =>
|
||||
@@ -20,7 +20,7 @@ describe('<ParDialogo />', () => {
|
||||
it('renders both voices of the exchange', () => {
|
||||
montar();
|
||||
expect(screen.getByText(/El siguiente salto no tiene mapa/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Me sumerjo hasta entender tu empresa desde dentro/)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Nos sumergimos hasta entender tu empresa desde dentro/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('attributes the answer to creeer, not to the reader', () => {
|
||||
@@ -56,7 +56,7 @@ describe('<ParDialogo />', () => {
|
||||
// document. `hidden` or `display: none` would take it away from screen
|
||||
// readers and from the static export the crawler sees — half the section's
|
||||
// message, gone, to buy an animation.
|
||||
const respuesta = screen.getByText(/Me sumerjo hasta entender tu empresa desde dentro/);
|
||||
const respuesta = screen.getByText(/Nos sumergimos hasta entender tu empresa desde dentro/);
|
||||
expect(respuesta).toBeInTheDocument();
|
||||
expect(respuesta.closest('[hidden]')).toBeNull();
|
||||
expect(screen.getByLabelText(/Respuesta de creeer a: Crecer/).className).toMatch(/opacity-0\b/);
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { Revelar } from '../Revelar';
|
||||
|
||||
// The stub in vitest.setup.ts is inert: it observes but never fires. This one
|
||||
// captures the callback so we can simulate the element entering the viewport.
|
||||
let disparar: (entradas: Partial<IntersectionObserverEntry>[]) => void;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal(
|
||||
'IntersectionObserver',
|
||||
class {
|
||||
constructor(cb: IntersectionObserverCallback) {
|
||||
disparar = (entradas) => cb(entradas as IntersectionObserverEntry[], this as never);
|
||||
}
|
||||
observe() {}
|
||||
unobserve() {}
|
||||
disconnect() {}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('<Revelar />', () => {
|
||||
it('no oculta nada por sí mismo', () => {
|
||||
// The hidden state is CSS-only, gated on `html.js`. If the component wrote
|
||||
// opacity 0 into the inline style we'd be back to the bug this redesign
|
||||
// fixes: 112 invisible elements in the export and a blank page whenever
|
||||
// the JS doesn't arrive.
|
||||
const { container } = render(<Revelar>Tu negocio no necesita más esfuerzo.</Revelar>);
|
||||
const el = container.firstElementChild as HTMLElement;
|
||||
expect(el.style.opacity).toBe('');
|
||||
expect(screen.getByText('Tu negocio no necesita más esfuerzo.')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('marca el bloque para que el CSS lo pueda gobernar', () => {
|
||||
const { container } = render(<Revelar>x</Revelar>);
|
||||
expect(container.firstElementChild).toHaveAttribute('data-revelar');
|
||||
});
|
||||
|
||||
it('escalona el retardo con el índice', () => {
|
||||
const { container } = render(<Revelar indice={3}>x</Revelar>);
|
||||
expect((container.firstElementChild as HTMLElement).style.getPropertyValue('--i')).toBe('3');
|
||||
});
|
||||
|
||||
it('se marca visible al entrar en viewport', () => {
|
||||
const { container } = render(<Revelar>x</Revelar>);
|
||||
const el = container.firstElementChild as HTMLElement;
|
||||
expect(el).not.toHaveAttribute('data-visible');
|
||||
disparar([{ isIntersecting: true, target: el }]);
|
||||
expect(el).toHaveAttribute('data-visible');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { Seccion } from '../Seccion';
|
||||
|
||||
describe('<Seccion />', () => {
|
||||
it('renders its content and its number', () => {
|
||||
render(<Seccion numero="02" paso={1}>Dónde estás</Seccion>);
|
||||
expect(screen.getByText('Dónde estás')).toBeInTheDocument();
|
||||
expect(screen.getByText('02')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('carries its mark at the step it is given', () => {
|
||||
const { container } = render(<Seccion numero="06" paso={5}>x</Seccion>);
|
||||
// Step 5 resolves the ≡: three of the nine lines stay visible.
|
||||
const visibles = Array.from(container.querySelectorAll('line')).filter(
|
||||
(l) => l.getAttribute('opacity') !== '0',
|
||||
);
|
||||
expect(visibles).toHaveLength(3);
|
||||
});
|
||||
|
||||
it('uses the manual gold on dark backgrounds, the readable gold on light ones', () => {
|
||||
const { container: oscuro } = render(<Seccion numero="03" paso={2} fondo="oscuro">x</Seccion>);
|
||||
expect(oscuro.querySelector('svg')?.getAttribute('class')).toContain('text-dorado');
|
||||
expect(oscuro.querySelector('svg')?.getAttribute('class')).not.toContain('text-dorado-texto');
|
||||
|
||||
const { container: claro } = render(<Seccion numero="04" paso={3}>x</Seccion>);
|
||||
expect(claro.querySelector('svg')?.getAttribute('class')).toContain('text-dorado-texto');
|
||||
});
|
||||
|
||||
it('is not announced to screen readers', () => {
|
||||
// It is document furniture, not content: the section's own heading
|
||||
// already says what it is about.
|
||||
const { container } = render(<Seccion numero="05" paso={4}>x</Seccion>);
|
||||
expect(container.querySelector('[aria-hidden="true"]')?.textContent).toContain('05');
|
||||
});
|
||||
|
||||
it('only becomes an anchor target when it has an id', () => {
|
||||
const { container } = render(<Seccion numero="06" paso={5} id="contacto">x</Seccion>);
|
||||
const seccion = container.querySelector('section');
|
||||
expect(seccion).toHaveAttribute('id', 'contacto');
|
||||
expect(seccion?.className).toContain('scroll-mt-24');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { SimboloMarca } from '../SimboloMarca';
|
||||
import { BARRAS_SIMBOLO } from '@/lib/simbolo';
|
||||
|
||||
describe('<SimboloMarca />', () => {
|
||||
it('is decorative: hidden from assistive tech', () => {
|
||||
const { container } = render(<SimboloMarca />);
|
||||
expect(container.querySelector('svg')).toHaveAttribute('aria-hidden', 'true');
|
||||
});
|
||||
|
||||
// The dueña's 28/jul instruction was that the ≡ has to read as an E. That
|
||||
// depends entirely on the proportions in lib/simbolo.ts — squat bars and
|
||||
// tight gaps turn it into a hamburger menu icon. Drawing from the canonical
|
||||
// geometry rather than hand-typed numbers is what keeps that guarantee, so
|
||||
// the test checks the bars really came from there.
|
||||
it('draws the three canonical bars, straight from lib/simbolo', () => {
|
||||
const { container } = render(<SimboloMarca />);
|
||||
const barras = container.querySelectorAll('rect');
|
||||
expect(barras).toHaveLength(3);
|
||||
barras.forEach((barra, i) => {
|
||||
expect(barra.getAttribute('x')).toBe(String(BARRAS_SIMBOLO[i].x));
|
||||
expect(barra.getAttribute('y')).toBe(String(BARRAS_SIMBOLO[i].y));
|
||||
expect(barra.getAttribute('width')).toBe(String(BARRAS_SIMBOLO[i].width));
|
||||
expect(barra.getAttribute('height')).toBe(String(BARRAS_SIMBOLO[i].height));
|
||||
});
|
||||
});
|
||||
|
||||
it('takes its colour from currentColor via className, not a hardcoded fill', () => {
|
||||
const { container } = render(<SimboloMarca className="text-dorado" />);
|
||||
const svg = container.querySelector('svg');
|
||||
expect(svg).toHaveAttribute('fill', 'currentColor');
|
||||
expect(svg).toHaveClass('text-dorado');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user