1import { readFileSync } from 'node:fs';2import { describe, it, expect } from 'vitest';3import { initFromPolicy, initGeneratorState, handleLengthChange } from '../generator-policy';45describe('generator site policy constraints', () => {6 it('renders the constraint note with a readable secondary-text token', () => {7 const css = readFileSync('src/popup/styles/popup.css', 'utf8');8 const rule = css.match(/\.peach-generator-constraint-note\s*\{([^}]*)\}/)?.[1];910 expect(rule).toContain('color: var(--peach-t1)');11 });1213 describe('Case 1 — no policy', () => {14 it('defaults to 64 when unconstrained and fresh', () => {15 const r = initFromPolicy(null, { currentLength: 64, userHasAdjusted: false });16 expect(r.length).toBe(64);17 });1819 it('preserves user length when no policy and user adjusted', () => {20 const r = initFromPolicy(null, { currentLength: 80, userHasAdjusted: true });21 expect(r.length).toBe(80);22 });2324 it('shows no constraint note', () => {25 const r = initFromPolicy(null, { currentLength: 64, userHasAdjusted: false });26 expect(r.constraintNote).toBeFalsy();27 });2829 it('leaves slider bounds at defaults', () => {30 const r = initFromPolicy(null, { currentLength: 64, userHasAdjusted: false });31 expect(r.sliderMin).toBe(8);32 expect(r.sliderMax).toBe(256);33 });34 });3536 describe('Case 2 — site maxLength, fresh open', () => {37 it('snaps to site maxLength', () => {38 const r = initFromPolicy(39 { maxLength: 100 },40 { currentLength: 64, userHasAdjusted: false },41 );42 expect(r.length).toBe(100);43 });4445 it('shows constraint note containing maxLength', () => {46 const r = initFromPolicy(47 { maxLength: 100 },48 { currentLength: 64, userHasAdjusted: false },49 );50 expect(r.constraintNote).toMatch(/100/);51 });5253 it('sets sliderMax to site maxLength', () => {54 const r = initFromPolicy(55 { maxLength: 100 },56 { currentLength: 64, userHasAdjusted: false },57 );58 expect(r.sliderMax).toBe(100);59 });60 });6162 describe('Case 3 — user above site max', () => {63 it('snaps DOWN to site max', () => {64 const r = initFromPolicy(65 { maxLength: 50 },66 { currentLength: 80, userHasAdjusted: true },67 );68 expect(r.length).toBe(50);69 });7071 it('shows constraint note after forced snap down', () => {72 const r = initFromPolicy(73 { maxLength: 50 },74 { currentLength: 80, userHasAdjusted: true },75 );76 expect(r.constraintNote).toBeTruthy();77 });78 });7980 describe('Case 4 — user below site max', () => {81 it('keeps user length when within bounds', () => {82 const r = initFromPolicy(83 { maxLength: 100 },84 { currentLength: 20, userHasAdjusted: true },85 );86 expect(r.length).toBe(20);87 });8889 it('shows no constraint note when within bounds', () => {90 const r = initFromPolicy(91 { maxLength: 100 },92 { currentLength: 20, userHasAdjusted: true },93 );94 expect(r.constraintNote).toBeFalsy();95 });96 });9798 describe('Case 5 — site minLength', () => {99 it('snaps UP to site minLength', () => {100 const r = initFromPolicy(101 { minLength: 12 },102 { currentLength: 8, userHasAdjusted: false },103 );104 expect(r.length).toBe(12);105 });106107 it('shows constraint note containing minLength', () => {108 const r = initFromPolicy(109 { minLength: 12 },110 { currentLength: 8, userHasAdjusted: false },111 );112 expect(r.constraintNote).toMatch(/12/);113 });114115 it('sets sliderMin to site minLength', () => {116 const r = initFromPolicy(117 { minLength: 12 },118 { currentLength: 8, userHasAdjusted: false },119 );120 expect(r.sliderMin).toBe(12);121 });122123 it('does not snap up when already above minLength', () => {124 const r = initFromPolicy(125 { minLength: 12 },126 { currentLength: 20, userHasAdjusted: true },127 );128 expect(r.length).toBe(20);129 });130 });131132 describe('Case 6 — both min and max', () => {133 it('snaps to min when below range', () => {134 const r = initFromPolicy(135 { minLength: 8, maxLength: 32 },136 { currentLength: 4, userHasAdjusted: false },137 );138 expect(r.length).toBe(8);139 });140141 it('snaps to max when above range', () => {142 const r = initFromPolicy(143 { minLength: 8, maxLength: 32 },144 { currentLength: 64, userHasAdjusted: true },145 );146 expect(r.length).toBe(32);147 });148149 it('keeps user length when within range', () => {150 const r = initFromPolicy(151 { minLength: 8, maxLength: 32 },152 { currentLength: 20, userHasAdjusted: true },153 );154 expect(r.length).toBe(20);155 });156157 it('defaults to maxLength on fresh open', () => {158 const r = initFromPolicy(159 { minLength: 8, maxLength: 32 },160 { currentLength: 64, userHasAdjusted: false },161 );162 expect(r.length).toBe(32);163 });164165 it('sets both sliderMin and sliderMax', () => {166 const r = initFromPolicy(167 { minLength: 8, maxLength: 32 },168 { currentLength: 20, userHasAdjusted: false },169 );170 expect(r.sliderMin).toBe(8);171 expect(r.sliderMax).toBe(32);172 });173 });174175 describe('userHasAdjusted flag', () => {176 it('is false on fresh generator open', () => {177 const state = initGeneratorState();178 expect(state.userHasAdjusted).toBe(false);179 });180181 it('becomes true after slider move', () => {182 let state = initGeneratorState();183 state = handleLengthChange(state, 80);184 expect(state.userHasAdjusted).toBe(true);185 });186187 it('false: policy snap applies', () => {188 const r = initFromPolicy(189 { maxLength: 100 },190 { currentLength: 64, userHasAdjusted: false },191 );192 expect(r.length).toBe(100);193 });194195 it('true: policy only enforces hard bounds', () => {196 const r = initFromPolicy(197 { maxLength: 100 },198 { currentLength: 30, userHasAdjusted: true },199 );200 expect(r.length).toBe(30);201 });202 });203});