Add GDPR compliant cookie banner and update footer/privacy policy to include GA and Clarity; added e2e and unit tests for cookie handling; updated snapshots
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/push/deploy Pipeline was successful

This commit is contained in:
2026-03-12 15:04:49 -03:00
parent 4ad45d5625
commit a5989b03b1
28 changed files with 607 additions and 9 deletions

View File

@@ -32,6 +32,7 @@
--font-family-heading: 'Fraunces', ui-serif, Georgia, 'Times New Roman', serif;
--font-size-base: 18px;
--font-size-xs: 14px;
--font-size-small: 15px;
--font-size-medium: 16px;
--font-size-large: 20px;
@@ -382,6 +383,14 @@ a {
&:active {
transform: translateY(0);
}
&.small {
border-radius: var(--border-radius-small);
font-size: var(--font-size-xs);
min-height: 36px;
min-width: fit-content;
padding: 0.5rem 1rem;
}
}
.icon-button {

View File

@@ -53,6 +53,13 @@
Terms of Service
</a>
</nav>
<p class="legal-notice">
We improve our products and advertising by using Google Analytics and
Microsoft Clarity to see how you use our website. By using our site, you agree
that we and Microsoft can collect and use this data. Our <a
href="/privacy-policy">privacy policy</a
> has more details.
</p>
</div>
</footer>
@@ -82,4 +89,22 @@
justify-content: center;
gap: var(--space-xs);
}
.legal-notice {
margin-top: var(--space-md);
font-size: var(--font-size-xs);
font-weight: var(--font-weight-normal);
color: var(--color-text-tertiary);
max-width: 100%;
}
.legal-notice a {
color: var(--color-primary);
text-decoration: underline;
text-underline-offset: 0.2em;
}
.legal-notice a:hover {
color: var(--color-primary-hover);
}
</style>

View File

@@ -32,7 +32,10 @@
<span
class={['mobile nav-header-logo', { 'page-home': bodyClass === 'page-home' }]}
>
<a href="/" class="logo-link">
<Wordmark />
<span class="sr-only">mifi Ventures home page</span>
</a>
</span>
<button
type="button"
@@ -64,7 +67,7 @@
]}
>
{#if page !== 'home'}
<a href="/">
<a href="/" class="logo-link">
<Wordmark />
<span class="sr-only">mifi Ventures home page</span>
</a>
@@ -155,6 +158,15 @@
display: inline-block;
}
}
& .logo-link {
text-decoration: none;
&:hover {
text-decoration: none;
border-bottom: none;
}
}
}
/* Hamburger toggle: mobile only, animates to X when open */

View File

@@ -1,20 +1,27 @@
/**
* Privacy Policy content for mifi Ventures. Used by /privacy-policy.
* Last updated: March 5, 2026. Includes Messaging Policy (SMS) for OpenPhone / A2P compliance.
* Last updated: March 12, 2026. Includes Messaging Policy (SMS) for OpenPhone / A2P compliance; Microsoft Clarity and Google Analytics.
*/
export interface LegalSectionLink {
href: string;
label: string;
}
export interface LegalSection {
id: string;
heading: string;
body: string[];
list?: string[];
/** Optional links to inject into body paragraphs (URLs in text become <a> with this label) */
links?: LegalSectionLink[];
/** Numbered sub-sections (e.g. Messaging Policy 16) */
subsections?: { title: string; body: string[]; list?: string[] }[];
}
export const privacyPolicy = {
title: 'Privacy Policy',
lastUpdated: 'March 5, 2026',
lastUpdated: 'March 12, 2026',
intro: [
'mifi Ventures LLC respects your privacy and is committed to protecting personal information shared through this website and related communications.',
'This policy explains what information we collect, how it is used, and how it is protected.',
@@ -56,6 +63,31 @@ export const privacyPolicy = {
'This information is used only to maintain the website, improve performance, and monitor security.',
],
},
{
id: 'analytics-and-tracking',
heading: 'Analytics and Tracking Technologies',
body: [
'We use third-party analytics and advertising tools to understand how visitors use this website and to improve our services.',
'We partner with Microsoft Clarity and Microsoft Advertising to capture how you use and interact with our website through behavioral metrics, heatmaps, and session replay to improve and market our products and services. Website usage data is captured using first- and third-party cookies and similar tracking technologies to determine the popularity of content and online activity. We also use this information for site optimization, security and fraud detection, and advertising.',
'For more information about how Microsoft collects and uses your data, see the Microsoft Privacy Statement: https://www.microsoft.com/privacy/privacystatement.',
'We also use Google Analytics to collect information about website usage, such as pages visited, time on site, and browser and device information. Google Analytics uses cookies and similar technologies to help us analyze how visitors use the site and to compile aggregated statistics.',
'You can learn more about how Google handles data in Google Analytics at: https://policies.google.com/privacy and https://policies.google.com/technologies/partner-sites.',
],
links: [
{
href: 'https://www.microsoft.com/privacy/privacystatement',
label: 'Microsoft Privacy Statement',
},
{
href: 'https://policies.google.com/privacy',
label: 'Google Privacy Policy',
},
{
href: 'https://policies.google.com/technologies/partner-sites',
label: 'How Google uses data from sites and apps',
},
],
},
{
id: 'how-we-use',
heading: 'How We Use Information',

View File

@@ -29,17 +29,12 @@
</script>
<svelte:head>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-36F29PDKRT"></script>
<script defer src="/assets/js/ga-init.js"></script>
<script
defer
src="https://analytics.mifi.holdings/script.js"
data-website-id="72ac01ce-e7fc-4582-8593-703f15add8d5"
></script>
<script defer src="/assets/js/umami-helper.js"></script>
<!-- Microsoft Clarity -->
<script defer src="https://www.clarity.ms/tag/vuo5q3yf79?ref=bwt"></script>
<title>{merged.title}</title>
<meta name="description" content={merged.description ?? ''} />
@@ -151,6 +146,7 @@
<script src="/assets/js/copyright-year.js" defer></script>
<script src="/assets/js/mobile-menu-helper.js" defer></script>
<script src="/assets/js/cookie-consent.js" defer></script>
</svelte:head>
<a href="#main" class="skip-link" data-umami-event="skip to main content"
@@ -158,6 +154,24 @@
>
{@render children()}
<Footer />
<div id="cookie-banner" class="cookie-banner" role="region" aria-label="Cookie consent">
<div class="cookie-banner-content">
<p class="cookie-notification-text">
We use first-party analytics and, if you accept, third-party tools (e.g.
Google, Microsoft) to understand usage and improve this site. You can accept
all or reject non-essential analytics.
<a href="/privacy-policy#analytics-and-tracking">Learn more</a>.
</p>
<div class="cookie-banner-actions">
<button type="button" class="btn btn-primary small" data-consent="accept"
>Accept all</button
>
<button type="button" class="btn btn-secondary small" data-consent="reject"
>Reject non-essential</button
>
</div>
</div>
</div>
<img
src="https://analytics.mifi.holdings/p/wQ9GYnLIg"
alt=""
@@ -166,3 +180,105 @@
role="presentation"
loading="eager"
/>
<style>
.cookie-banner {
position: fixed;
inset-inline: 0;
bottom: 0;
z-index: 1000;
display: none;
background-color: var(--color-bg-elevated, var(--color-bg));
color: var(--color-text);
border-top: 1px solid var(--color-border);
box-shadow: 0 -8px 24px rgba(15, 23, 42, 0.16);
&:global(.is-visible) {
display: block;
}
}
.cookie-banner-content {
container: cookie-banner / inline-size;
max-width: var(--max-narrow-width);
margin: 0 auto;
padding: var(--space-md) var(--space-md);
display: flex;
flex-direction: column;
gap: var(--space-sm);
@media (min-width: 768px) {
flex-direction: row;
align-items: center;
justify-content: space-between;
}
}
.cookie-notification-text {
margin: 0;
font-size: var(--font-size-small);
line-height: var(--line-height-base);
@container cookie-banner (width >= 644px) {
flex: 1 1 auto;
}
@container cookie-banner (width < 644px) {
flex: 0 0 100%;
text-align: center;
width: 100%;
}
& a {
color: var(--color-primary);
text-decoration: underline;
text-underline-offset: 0.16em;
&:hover {
color: var(--color-primary-hover);
}
}
}
.cookie-banner-actions {
display: flex;
flex-flow: column nowrap;
justify-content: flex-start;
@container cookie-banner (width >= 644px) {
flex: 0 0 auto;
gap: var(--space-xs);
}
@container cookie-banner (width < 644px) {
flex: 0 0 100%;
gap: var(--space-sm);
justify-content: center;
width: 100%;
& .btn {
max-width: 100%;
width: 100%;
}
}
& [data-consent='accept'] {
background-color: var(--color-primary);
color: var(--color-bg);
&:hover {
background-color: var(--color-primary-hover);
}
}
& [data-consent='reject'] {
background-color: transparent;
color: var(--color-text);
border-color: var(--color-primary);
&:hover {
background-color: var(--color-surface-subtle, rgba(148, 163, 184, 0.16));
}
}
}
</style>

View File

@@ -1,8 +1,30 @@
<script lang="ts">
import Navigation from '$lib/components/Navigation.svelte';
import { privacyPolicy } from '$lib/data/privacy-policy';
import type { LegalSectionLink } from '$lib/data/privacy-policy';
const navItems = [{ label: 'Home', href: '/', umamiEventLabel: 'home' }];
type BodySegment = string | { type: 'link'; href: string; label: string };
/** Splits a paragraph by link URLs and returns text/link segments for rendering. */
function linkify(para: string, links: LegalSectionLink[]): BodySegment[] {
const matches: { index: number; link: LegalSectionLink }[] = [];
for (const link of links) {
const idx = para.indexOf(link.href);
if (idx !== -1) matches.push({ index: idx, link });
}
matches.sort((a, b) => a.index - b.index);
const result: BodySegment[] = [];
let last = 0;
for (const { index, link } of matches) {
if (index > last) result.push(para.slice(last, index));
result.push({ type: 'link', href: link.href, label: link.label });
last = index + link.href.length;
}
if (last < para.length) result.push(para.slice(last));
return result.length ? result : [para];
}
</script>
<Navigation items={navItems} page="privacy-policy" />
@@ -36,6 +58,16 @@
<a href="https://mifi.ventures" rel="noopener noreferrer"
>https://mifi.ventures</a
>
{:else if section.links}
{#each linkify(para, section.links) as segment}
{#if typeof segment === 'string'}
{segment}
{:else}
<a href={segment.href} rel="noopener noreferrer"
>{segment.label}</a
>
{/if}
{/each}
{:else}
{para}
{/if}
@@ -116,6 +148,16 @@
margin: 0 0 var(--space-md) 0;
}
.legal-content a {
color: var(--color-primary);
text-decoration: underline;
text-underline-offset: 0.2em;
}
.legal-content a:hover {
color: var(--color-primary-hover);
}
.legal-section {
margin-bottom: var(--space-xl);
}

View File

@@ -0,0 +1,5 @@
{
"name": "mifi Ventures",
"description": "mifi Ventures is a consulting firm that helps early-stage SaaS companies build and scale their businesses.",
"url": "https://mifi.ventures"
}

View File

@@ -0,0 +1,113 @@
// Cookie consent banner & third-party analytics loader
// - Stores preference in localStorage
// - Shows a bottom banner until user accepts or rejects
// - Loads Google Analytics and Microsoft Clarity only when accepted
(function () {
'use strict';
var STORAGE_KEY = 'mifi-ventures-cookie-consent';
var BANNER_ID = 'cookie-banner';
var BANNER_VISIBLE_CLASS = 'is-visible';
var hasLoadedThirdParty = false;
function loadThirdPartyAnalytics() {
if (hasLoadedThirdParty) return;
hasLoadedThirdParty = true;
// Google Analytics (gtag.js + ga-init.js)
try {
var gtagScript = document.createElement('script');
gtagScript.async = true;
gtagScript.src =
'https://www.googletagmanager.com/gtag/js?id=G-36F29PDKRT';
gtagScript.onload = function () {
// Load the existing ga-init.js helper once gtag is ready
var gaInit = document.createElement('script');
gaInit.defer = true;
gaInit.src = '/assets/js/ga-init.js';
document.head.appendChild(gaInit);
};
document.head.appendChild(gtagScript);
} catch (e) {
// Fail silently analytics are non-essential
console.error('Failed to load Google Analytics', e);
}
// Microsoft Clarity
try {
var clarityScript = document.createElement('script');
clarityScript.defer = true;
clarityScript.src = 'https://www.clarity.ms/tag/vuo5q3yf79?ref=bwt';
document.head.appendChild(clarityScript);
} catch (e2) {
console.error('Failed to load Microsoft Clarity', e2);
}
}
function hideBanner(banner) {
if (!banner) return;
banner.classList.remove(BANNER_VISIBLE_CLASS);
}
function showBanner(banner) {
if (!banner) return;
banner.classList.add(BANNER_VISIBLE_CLASS);
}
function init() {
var banner = document.getElementById(BANNER_ID);
if (!banner) return;
var acceptBtn = banner.querySelector('[data-consent="accept"]');
var rejectBtn = banner.querySelector('[data-consent="reject"]');
var pref;
try {
pref = window.localStorage.getItem(STORAGE_KEY);
} catch (_) {
pref = null;
}
if (pref === 'accept') {
hideBanner(banner);
loadThirdPartyAnalytics();
} else if (pref === 'reject') {
hideBanner(banner);
} else {
showBanner(banner);
}
if (acceptBtn) {
acceptBtn.addEventListener('click', function () {
try {
window.localStorage.setItem(STORAGE_KEY, 'accept');
} catch (_) {
// ignore
}
hideBanner(banner);
loadThirdPartyAnalytics();
});
}
if (rejectBtn) {
rejectBtn.addEventListener('click', function () {
try {
window.localStorage.setItem(STORAGE_KEY, 'reject');
} catch (_) {
// ignore
}
hideBanner(banner);
});
}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();

View File

@@ -14,6 +14,12 @@ export default {
'no-descending-specificity': null,
'media-feature-name-value-no-unknown': null,
'selector-pseudo-element-colon-notation': null,
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['global'],
},
],
},
overrides: [
{

View File

@@ -0,0 +1,46 @@
import { test, expect } from '@playwright/test';
test.describe('visual regression', () => {
test('cookie banner is visible', async ({ page }) => {
await page.goto('/');
await expect(page.locator('#cookie-banner')).toBeVisible();
});
test('cookie banner is not visible when choices are accepted', async ({ page }) => {
await page.goto('/');
await page.evaluate(() => {
localStorage.setItem('mifi-ventures-cookie-consent', 'accept');
});
await page.goto('/');
await expect(page.locator('#cookie-banner')).not.toBeVisible();
});
test('cookie banner is not visible when choices are rejected', async ({ page }) => {
await page.goto('/');
await page.evaluate(() => {
localStorage.setItem('mifi-ventures-cookie-consent', 'reject');
});
await page.goto('/');
await expect(page.locator('#cookie-banner')).not.toBeVisible();
});
test('cookie banner is hidden when user selects accept', async ({ page }) => {
await page.goto('/');
await expect(page.locator('#cookie-banner')).toBeVisible();
await page.locator('[data-consent="accept"]').click();
await expect(page.locator('#cookie-banner')).not.toBeVisible();
await expect(await page.evaluate(() => localStorage.getItem('mifi-ventures-cookie-consent'))).toBe('accept');
});
test('cookie banner is hidden when user selects reject', async ({ page }) => {
await page.goto('/');
await expect(page.locator('#cookie-banner')).toBeVisible();
await page.locator('[data-consent="reject"]').click();
await expect(page.locator('#cookie-banner')).not.toBeVisible();
await expect(await page.evaluate(() => localStorage.getItem('mifi-ventures-cookie-consent'))).toBe('reject');
});
});

View File

@@ -0,0 +1,119 @@
/**
* @vitest-environment jsdom
*/
import { readFileSync } from 'node:fs';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
const __dirname = dirname(fileURLToPath(import.meta.url));
const SCRIPT_PATH = join(__dirname, '../../static/assets/js/cookie-consent.js');
function createBannerDOM() {
const banner = document.createElement('div');
banner.id = 'cookie-banner';
const acceptBtn = document.createElement('button');
acceptBtn.type = 'button';
acceptBtn.dataset.consent = 'accept';
const rejectBtn = document.createElement('button');
rejectBtn.type = 'button';
rejectBtn.dataset.consent = 'reject';
banner.appendChild(acceptBtn);
banner.appendChild(rejectBtn);
document.body.appendChild(banner);
return { banner, acceptBtn, rejectBtn };
}
describe('cookie-consent.js', () => {
const STORAGE_KEY = 'mifi-ventures-cookie-consent';
let dom: ReturnType<typeof createBannerDOM>;
beforeEach(() => {
// Ensure a fresh DOM and storage for each test
document.body.innerHTML = '';
window.localStorage.clear();
dom = createBannerDOM();
const code = readFileSync(SCRIPT_PATH, 'utf8');
// eslint-disable-next-line no-eval
eval(code);
document.dispatchEvent(new Event('DOMContentLoaded'));
});
afterEach(() => {
dom.banner.remove();
vi.restoreAllMocks();
window.localStorage.clear();
});
it('shows banner when no preference is stored', () => {
expect(dom.banner.classList.contains('is-visible')).toBe(true);
});
it('hides banner and loads analytics when preference is "accept"', () => {
document.body.innerHTML = '';
window.localStorage.setItem(STORAGE_KEY, 'accept');
dom = createBannerDOM();
const appendChildSpy = vi.spyOn(document.head, 'appendChild');
const code = readFileSync(SCRIPT_PATH, 'utf8');
// eslint-disable-next-line no-eval
eval(code);
document.dispatchEvent(new Event('DOMContentLoaded'));
expect(dom.banner.classList.contains('is-visible')).toBe(false);
expect(appendChildSpy).toHaveBeenCalled();
const urls = appendChildSpy.mock.calls
.map((args) => (args[0] as HTMLScriptElement).src)
.filter(Boolean);
expect(urls.some((src) => src.includes('googletagmanager.com'))).toBe(true);
});
it('hides banner when preference is "reject" and does not load analytics', () => {
document.body.innerHTML = '';
window.localStorage.setItem(STORAGE_KEY, 'reject');
dom = createBannerDOM();
const appendChildSpy = vi.spyOn(document.head, 'appendChild');
const code = readFileSync(SCRIPT_PATH, 'utf8');
// eslint-disable-next-line no-eval
eval(code);
document.dispatchEvent(new Event('DOMContentLoaded'));
expect(dom.banner.classList.contains('is-visible')).toBe(false);
const urls = appendChildSpy.mock.calls
.map((args) => (args[0] as HTMLScriptElement).src)
.filter(Boolean);
expect(urls.some((src) => src.includes('googletagmanager.com'))).toBe(false);
});
it('stores "accept" in localStorage, hides banner, and loads analytics on accept click', () => {
const appendChildSpy = vi.spyOn(document.head, 'appendChild');
dom.acceptBtn.click();
expect(window.localStorage.getItem(STORAGE_KEY)).toBe('accept');
expect(dom.banner.classList.contains('is-visible')).toBe(false);
expect(appendChildSpy).toHaveBeenCalled();
});
it('stores "reject" in localStorage and hides banner on reject click', () => {
const appendChildSpy = vi.spyOn(document.head, 'appendChild');
dom.rejectBtn.click();
expect(window.localStorage.getItem(STORAGE_KEY)).toBe('reject');
expect(dom.banner.classList.contains('is-visible')).toBe(false);
const urls = appendChildSpy.mock.calls
.map((args) => (args[0] as HTMLScriptElement).src)
.filter(Boolean);
expect(urls.some((src) => src.includes('googletagmanager.com'))).toBe(false);
});
});

View File

@@ -7,6 +7,14 @@ test.describe('visual regression', () => {
await expect(page.locator('h1')).toBeVisible();
await expect(page.locator('#main')).toBeVisible();
await expect(page.locator('.footer')).toBeVisible();
await expect(page.locator('#cookie-banner')).toBeVisible();
await page.evaluate(() => {
localStorage.setItem('mifi-ventures-cookie-consent', 'reject');
});
await page.goto('/');
await expect(page.locator('#cookie-banner')).not.toBeVisible();
await expect(page).toHaveScreenshot('home.png', { fullPage: true });
});
@@ -15,6 +23,15 @@ test.describe('visual regression', () => {
await expect(page).toHaveTitle(/SaaS Architecture Services | mifi Ventures/);
await expect(page.locator('h1')).toBeVisible();
await expect(page.locator('#main')).toBeVisible();
await expect(page.locator('.footer')).toBeVisible();
await expect(page.locator('#cookie-banner')).toBeVisible();
await page.evaluate(() => {
localStorage.setItem('mifi-ventures-cookie-consent', 'reject');
});
await page.goto('/services');
await expect(page.locator('#cookie-banner')).not.toBeVisible();
await expect(page).toHaveScreenshot('services.png', { fullPage: true });
});
@@ -23,6 +40,16 @@ test.describe('visual regression', () => {
await expect(page).toHaveTitle(/SaaS Product Architecture Consultant | mifi Ventures/);
await expect(page.locator('h1')).toBeVisible();
await expect(page.locator('#main')).toBeVisible();
await expect(page.locator('#faq')).toBeVisible();
await expect(page.locator('.footer')).toBeVisible();
await expect(page.locator('#cookie-banner')).toBeVisible();
await page.evaluate(() => {
localStorage.setItem('mifi-ventures-cookie-consent', 'reject');
});
await page.goto('/services/hands-on-saas-architecture-consultant');
await expect(page.locator('#cookie-banner')).not.toBeVisible();
await expect(page).toHaveScreenshot('services-hands-on-saas-architecture-consultant.png', { fullPage: true });
});
@@ -31,6 +58,16 @@ test.describe('visual regression', () => {
await expect(page).toHaveTitle(/MVP Architecture & Launch Consultant | mifi Ventures/);
await expect(page.locator('h1')).toBeVisible();
await expect(page.locator('#main')).toBeVisible();
await expect(page.locator('#faq')).toBeVisible();
await expect(page.locator('.footer')).toBeVisible();
await expect(page.locator('#cookie-banner')).toBeVisible();
await page.evaluate(() => {
localStorage.setItem('mifi-ventures-cookie-consent', 'reject');
});
await page.goto('/services/mvp-architecture-and-launch');
await expect(page.locator('#cookie-banner')).not.toBeVisible();
await expect(page).toHaveScreenshot('services-mvp-architecture-and-launch.png', { fullPage: true });
});
@@ -39,6 +76,16 @@ test.describe('visual regression', () => {
await expect(page).toHaveTitle(/Fractional CTO for Early-Stage SaaS | mifi Ventures/);
await expect(page.locator('h1')).toBeVisible();
await expect(page.locator('#main')).toBeVisible();
await expect(page.locator('#faq')).toBeVisible();
await expect(page.locator('.footer')).toBeVisible();
await expect(page.locator('#cookie-banner')).toBeVisible();
await page.evaluate(() => {
localStorage.setItem('mifi-ventures-cookie-consent', 'reject');
});
await page.goto('/services/fractional-cto-for-early-stage-saas');
await expect(page.locator('#cookie-banner')).not.toBeVisible();
await expect(page).toHaveScreenshot('services-fractional-cto-for-early-stage-saas.png', { fullPage: true });
});
@@ -47,6 +94,16 @@ test.describe('visual regression', () => {
await expect(page).toHaveTitle(/Startup Infrastructure Strategy | mifi Ventures/);
await expect(page.locator('h1')).toBeVisible();
await expect(page.locator('#main')).toBeVisible();
await expect(page.locator('#faq')).toBeVisible();
await expect(page.locator('.footer')).toBeVisible();
await expect(page.locator('#cookie-banner')).toBeVisible();
await page.evaluate(() => {
localStorage.setItem('mifi-ventures-cookie-consent', 'reject');
});
await page.goto('/services/stage-aligned-infrastructure');
await expect(page.locator('#cookie-banner')).not.toBeVisible();
await expect(page).toHaveScreenshot('services-stage-aligned-infrastructure.png', { fullPage: true });
});
@@ -56,6 +113,14 @@ test.describe('visual regression', () => {
await expect(page.locator('h1')).toBeVisible();
await expect(page.locator('#main')).toBeVisible();
await expect(page.locator('.footer')).toBeVisible();
await expect(page.locator('#cookie-banner')).toBeVisible();
await page.evaluate(() => {
localStorage.setItem('mifi-ventures-cookie-consent', 'reject');
});
await page.goto('/privacy-policy');
await expect(page.locator('#cookie-banner')).not.toBeVisible();
await expect(page).toHaveScreenshot('privacy-policy.png', { fullPage: true });
});
@@ -65,6 +130,14 @@ test.describe('visual regression', () => {
await expect(page.locator('h1')).toBeVisible();
await expect(page.locator('#main')).toBeVisible();
await expect(page.locator('.footer')).toBeVisible();
await expect(page.locator('#cookie-banner')).toBeVisible();
await page.evaluate(() => {
localStorage.setItem('mifi-ventures-cookie-consent', 'reject');
});
await page.goto('/terms-of-service');
await expect(page.locator('#cookie-banner')).not.toBeVisible();
await expect(page).toHaveScreenshot('terms-of-service.png', { fullPage: true });
});
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 538 KiB

After

Width:  |  Height:  |  Size: 554 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 548 KiB

After

Width:  |  Height:  |  Size: 571 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 KiB

After

Width:  |  Height:  |  Size: 704 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 KiB

After

Width:  |  Height:  |  Size: 715 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 381 KiB

After

Width:  |  Height:  |  Size: 397 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 375 KiB

After

Width:  |  Height:  |  Size: 398 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 789 KiB

After

Width:  |  Height:  |  Size: 805 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 794 KiB

After

Width:  |  Height:  |  Size: 817 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 911 KiB

After

Width:  |  Height:  |  Size: 927 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 918 KiB

After

Width:  |  Height:  |  Size: 941 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 687 KiB

After

Width:  |  Height:  |  Size: 703 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 672 KiB

After

Width:  |  Height:  |  Size: 695 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 870 KiB

After

Width:  |  Height:  |  Size: 886 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 869 KiB

After

Width:  |  Height:  |  Size: 891 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 KiB

After

Width:  |  Height:  |  Size: 367 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 351 KiB

After

Width:  |  Height:  |  Size: 374 KiB