@@ -7,27 +7,42 @@ import userEvent from '@testing-library/user-event';
77import type { Token } from 'interlinearizer' ;
88import { TokenChip } from '../../components/TokenChip' ;
99
10- const WORD_TOKEN : Token = {
10+ const WORD_TOKEN = {
1111 id : 'GEN 1:1:0' ,
1212 surfaceText : 'hello' ,
1313 writingSystem : 'en' ,
1414 type : 'word' ,
1515 charStart : 0 ,
1616 charEnd : 5 ,
17- } ;
17+ } satisfies Token ;
1818
19- const PUNCT_TOKEN : Token = {
19+ const PUNCT_TOKEN = {
2020 id : 'GEN 1:1:5' ,
2121 surfaceText : '.' ,
2222 writingSystem : 'en' ,
2323 type : 'punctuation' ,
2424 charStart : 5 ,
2525 charEnd : 6 ,
26- } ;
26+ } satisfies Token ;
27+
28+ /**
29+ * Minimal required props for a word-token `TokenChip`. Spread into render calls so tests only need
30+ * to override what they actually care about.
31+ *
32+ * @returns An object with all required word-token props set to no-op stubs.
33+ */
34+ function requiredWordProps ( ) {
35+ return {
36+ token : WORD_TOKEN ,
37+ gloss : '' ,
38+ onFocus : jest . fn ( ) ,
39+ onGlossChange : jest . fn ( ) ,
40+ } as const ;
41+ }
2742
2843describe ( 'TokenChip' , ( ) => {
2944 it ( 'renders the surface text for a word token' , ( ) => {
30- render ( < TokenChip token = { WORD_TOKEN } /> ) ;
45+ render ( < TokenChip { ... requiredWordProps ( ) } /> ) ;
3146 expect ( screen . getByText ( 'hello' ) ) . toBeInTheDocument ( ) ;
3247 } ) ;
3348
@@ -37,7 +52,7 @@ describe('TokenChip', () => {
3752 } ) ;
3853
3954 it ( 'applies a border class to word tokens' , ( ) => {
40- render ( < TokenChip token = { WORD_TOKEN } /> ) ;
55+ render ( < TokenChip { ... requiredWordProps ( ) } /> ) ;
4156 // The outer container holds the border; the inner span is just the surface text
4257 const outer = screen . getByText ( 'hello' ) . closest ( 'span' ) ?. parentElement ;
4358 expect ( outer ?. className ) . toContain ( 'tw:border' ) ;
@@ -50,14 +65,14 @@ describe('TokenChip', () => {
5065 } ) ;
5166
5267 it ( 'renders word and punctuation tokens as inline spans' , ( ) => {
53- const { container : wc } = render ( < TokenChip token = { WORD_TOKEN } /> ) ;
68+ const { container : wc } = render ( < TokenChip { ... requiredWordProps ( ) } /> ) ;
5469 const { container : pc } = render ( < TokenChip token = { PUNCT_TOKEN } /> ) ;
5570 expect ( wc . querySelector ( 'span' ) ) . toBeInTheDocument ( ) ;
5671 expect ( pc . querySelector ( 'span' ) ) . toBeInTheDocument ( ) ;
5772 } ) ;
5873
5974 it ( 'renders a gloss input for word tokens' , ( ) => {
60- render ( < TokenChip token = { WORD_TOKEN } /> ) ;
75+ render ( < TokenChip { ... requiredWordProps ( ) } /> ) ;
6176 expect ( screen . getByRole ( 'textbox' , { name : 'Gloss for hello' } ) ) . toBeInTheDocument ( ) ;
6277 } ) ;
6378
@@ -67,41 +82,28 @@ describe('TokenChip', () => {
6782 } ) ;
6883
6984 it ( 'shows the current gloss value in the input' , ( ) => {
70- render ( < TokenChip token = { WORD_TOKEN } gloss = "in" /> ) ;
85+ render ( < TokenChip { ... requiredWordProps ( ) } gloss = "in" /> ) ;
7186 expect ( screen . getByRole ( 'textbox' , { name : 'Gloss for hello' } ) ) . toHaveValue ( 'in' ) ;
7287 } ) ;
7388
74- it ( 'shows an empty input when no gloss is provided ' , ( ) => {
75- render ( < TokenChip token = { WORD_TOKEN } /> ) ;
89+ it ( 'shows an empty string in the input when gloss is empty ' , ( ) => {
90+ render ( < TokenChip { ... requiredWordProps ( ) } gloss = "" /> ) ;
7691 expect ( screen . getByRole ( 'textbox' , { name : 'Gloss for hello' } ) ) . toHaveValue ( '' ) ;
7792 } ) ;
7893
7994 it ( 'calls onGlossChange for each keystroke' , async ( ) => {
8095 const handleChange = jest . fn ( ) ;
81- render ( < TokenChip token = { WORD_TOKEN } onGlossChange = { handleChange } /> ) ;
96+ render ( < TokenChip { ... requiredWordProps ( ) } onGlossChange = { handleChange } /> ) ;
8297 await userEvent . type ( screen . getByRole ( 'textbox' , { name : 'Gloss for hello' } ) , 'in' ) ;
8398 expect ( handleChange ) . toHaveBeenCalledTimes ( 2 ) ;
8499 expect ( handleChange ) . toHaveBeenNthCalledWith ( 1 , 'i' ) ;
85100 expect ( handleChange ) . toHaveBeenNthCalledWith ( 2 , 'n' ) ;
86101 } ) ;
87102
88- it ( 'does not throw when onGlossChange is omitted and the user types' , async ( ) => {
89- render ( < TokenChip token = { WORD_TOKEN } /> ) ;
90- await userEvent . type ( screen . getByRole ( 'textbox' , { name : 'Gloss for hello' } ) , 'in' ) ;
91- // No assertion needed — test passes if no error is thrown
92- } ) ;
93-
94103 it ( 'calls onFocus when the input is focused' , async ( ) => {
95104 const handleFocus = jest . fn ( ) ;
96- render ( < TokenChip token = { WORD_TOKEN } onFocus = { handleFocus } /> ) ;
105+ render ( < TokenChip { ... requiredWordProps ( ) } onFocus = { handleFocus } /> ) ;
97106 await userEvent . click ( screen . getByRole ( 'textbox' , { name : 'Gloss for hello' } ) ) ;
98107 expect ( handleFocus ) . toHaveBeenCalledTimes ( 1 ) ;
99108 } ) ;
100-
101- it ( 'does not throw when onFocus is omitted' , async ( ) => {
102- render ( < TokenChip token = { WORD_TOKEN } /> ) ;
103- await userEvent . click ( screen . getByRole ( 'textbox' , { name : 'Gloss for hello' } ) ) ;
104- await userEvent . tab ( ) ;
105- // No assertion needed — test passes if no error is thrown
106- } ) ;
107109} ) ;
0 commit comments