@@ -222,6 +222,149 @@ void main() {
222222 });
223223 });
224224
225+ group ('code variants' , () {
226+ testWidgets ('every variant resolves the pinned fill and foreground' , (
227+ tester,
228+ ) async {
229+ for (final brightness in Brightness .values) {
230+ final tokens = await _accentTokens (tester, brightness);
231+ for (final highContrast in [false , true ]) {
232+ for (final variant in FortalCodeVariant .values) {
233+ await _pump (
234+ tester,
235+ FortalCode (
236+ '${variant .name }-$highContrast ' ,
237+ variant: variant,
238+ highContrast: highContrast,
239+ ),
240+ brightness: brightness,
241+ );
242+ final fill = _boxColor (_surface (tester));
243+ final fg = _text (
244+ tester,
245+ '${variant .name }-$highContrast ' ,
246+ ).style? .color;
247+
248+ switch (variant) {
249+ case FortalCodeVariant .solid:
250+ expect (fill, highContrast ? tokens.accent12 : tokens.accentA9);
251+ expect (
252+ fg,
253+ highContrast ? tokens.accent1 : tokens.accentContrast,
254+ );
255+ case FortalCodeVariant .soft:
256+ expect (fill, tokens.accentA3);
257+ expect (fg, highContrast ? tokens.accent12 : tokens.accentA11);
258+ case FortalCodeVariant .outline:
259+ expect (fg, highContrast ? tokens.accent12 : tokens.accentA11);
260+ case FortalCodeVariant .ghost:
261+ // Upstream gates ghost's colour on an explicit accent, so an
262+ // opt-out ghost inherits the ambient foreground.
263+ expect (fill, isNull);
264+ expect (fg, isNull);
265+ }
266+ }
267+ }
268+ }
269+ });
270+
271+ testWidgets ('outline draws one ring, two at high contrast' , (tester) async {
272+ final tokens = await _accentTokens (tester, Brightness .light);
273+ for (final highContrast in [false , true ]) {
274+ await _pump (
275+ tester,
276+ FortalCode .outline (
277+ 'ring-$highContrast ' ,
278+ size: FortalTextSize .size9,
279+ highContrast: highContrast,
280+ ),
281+ );
282+ final shadows = _surface (
283+ tester,
284+ ).containerEffects! .behindContent! .shadows;
285+ expect (shadows, hasLength (highContrast ? 2 : 1 ));
286+ expect (
287+ shadows.first.color,
288+ highContrast ? tokens.accentA7 : tokens.accentA8,
289+ );
290+ for (final shadow in shadows) {
291+ expect (shadow.spreadRadius, closeTo (0.033 * 60 * 0.95 * 0.95 , 1e-9 ));
292+ }
293+ }
294+ });
295+
296+ testWidgets ('ghost gains the accent foreground only when opted in' , (
297+ tester,
298+ ) async {
299+ final tokens = await _accentTokens (tester, Brightness .light);
300+ await _pump (
301+ tester,
302+ const Column (
303+ children: [
304+ FortalCode .ghost ('plain ghost' ),
305+ FortalCode .ghost ('accent ghost' , accent: true ),
306+ FortalCode .ghost (
307+ 'accent hc ghost' ,
308+ accent: true ,
309+ highContrast: true ,
310+ ),
311+ ],
312+ ),
313+ );
314+ expect (_text (tester, 'plain ghost' ).style? .color, isNull);
315+ expect (_text (tester, 'accent ghost' ).style? .color, tokens.accentA11);
316+ expect (_text (tester, 'accent hc ghost' ).style? .color, tokens.accent12);
317+ });
318+
319+ testWidgets ('truncation and kbd min-width reach the rendered box' , (
320+ tester,
321+ ) async {
322+ await _pump (
323+ tester,
324+ const SizedBox (
325+ width: 60 ,
326+ child: FortalCode .soft ('a very long code sample' , truncate: true ),
327+ ),
328+ );
329+ final code = _text (tester, 'a very long code sample' );
330+ expect (code.maxLines, 1 );
331+ expect (code.overflow, TextOverflow .ellipsis);
332+
333+ await _pump (
334+ tester,
335+ const FortalKbd .soft ('x' , size: FortalTextSize .size2),
336+ );
337+ expect (
338+ _surface (tester).container.spec.constraints? .minWidth,
339+ closeTo (1.75 * 14 * 0.8 , 1e-9 ),
340+ );
341+ });
342+ });
343+
344+ group ('focus' , () {
345+ testWidgets ('focused link draws the outline without moving layout' , (
346+ tester,
347+ ) async {
348+ final focusNode = FocusNode ();
349+ addTearDown (focusNode.dispose);
350+ await _pump (
351+ tester,
352+ FortalLink ('focus me' , focusNode: focusNode, onPressed: _noop),
353+ );
354+ final idleSize = tester.getSize (find.text ('focus me' ));
355+
356+ focusNode.requestFocus ();
357+ await tester.pump ();
358+
359+ final effects = _surface (tester).containerEffects! ;
360+ expect (effects.outline.width, 2 );
361+ expect (effects.outlineOffset, 2 );
362+ expect (tester.getSize (find.text ('focus me' )), idleSize);
363+ // Focus replaces the underline rather than stacking both.
364+ expect (_underlined (tester, 'focus me' ), isFalse);
365+ });
366+ });
367+
225368 group ('theme' , () {
226369 for (final brightness in Brightness .values) {
227370 testWidgets ('kbd classic keeps all six ${brightness .name } layers' , (
@@ -438,5 +581,57 @@ BadgeSpec _surface(WidgetTester tester) {
438581 return widget.style.resolve (tester.element (find.byType (RemixBadge ))).spec;
439582}
440583
584+ Color ? _boxColor (BadgeSpec spec) =>
585+ (spec.container.spec.decoration as BoxDecoration ? )? .color;
586+
587+ Future <
588+ ({
589+ Color accent1,
590+ Color accent12,
591+ Color accentA3,
592+ Color accentA7,
593+ Color accentA8,
594+ Color accentA9,
595+ Color accentA11,
596+ Color accentContrast,
597+ })
598+ >
599+ _accentTokens (WidgetTester tester, Brightness brightness) async {
600+ late ({
601+ Color accent1,
602+ Color accent12,
603+ Color accentA3,
604+ Color accentA7,
605+ Color accentA8,
606+ Color accentA9,
607+ Color accentA11,
608+ Color accentContrast,
609+ })
610+ result;
611+ await tester.pumpWidget (
612+ FortalScope (
613+ brightness: brightness,
614+ child: Builder (
615+ builder: (context) {
616+ Color token (ColorToken value) => MixScope .tokenOf (value, context);
617+ result = (
618+ accent1: token (FortalTokens .accent1),
619+ accent12: token (FortalTokens .accent12),
620+ accentA3: token (FortalTokens .accentA3),
621+ accentA7: token (FortalTokens .accentA7),
622+ accentA8: token (FortalTokens .accentA8),
623+ accentA9: token (FortalTokens .accentA9),
624+ accentA11: token (FortalTokens .accentA11),
625+ accentContrast: token (FortalTokens .accentContrast),
626+ );
627+ return const SizedBox .shrink ();
628+ },
629+ ),
630+ ),
631+ );
632+
633+ return result;
634+ }
635+
441636bool _underlined (WidgetTester tester, String text) =>
442637 _text (tester, text).style? .decoration == TextDecoration .underline;
0 commit comments