blob: 06b629f34741615dff3d327de278f5dd7312271f [file] [log] [blame]
jason westover313d15c2025-08-18 17:16:01 -05001/* eslint-env jest */
2import { config } from '@vue/test-utils';
3
4// Make Math.random deterministic for stable snapshots (e.g., IDs in components)
5jest.spyOn(Math, 'random').mockReturnValue(0.123456789);
6
7// Provide a minimal vue-router API for tests that import it
8jest.mock('vue-router', () => ({
9 createRouter: () => ({}),
10 createMemoryHistory: () => ({}),
11}));
12
13// Create a shared i18n instance for component tests
14// This provides a working i18n plugin for components that use useI18n()
15import { createI18n } from 'vue-i18n';
16import enUS from '@/locales/en-US.json';
17
18// Mock the default export of @/i18n to provide a working i18n instance.
19// In Jest, require.context doesn't work, so the real module would have no messages.
20// Note: The i18n.*.spec.js tests use jest.unmock('@/i18n') to test the real module.
21jest.mock('@/i18n', () => {
22 const { createI18n: create } = require('vue-i18n');
23 // eslint-disable-next-line global-require
24 const messages = require('@/locales/en-US.json');
25 const mockI18n = create({
26 legacy: false,
27 locale: 'en-US',
28 fallbackLocale: 'en-US',
29 globalInjection: true,
30 messages: {
31 'en-US': messages.default || messages,
32 en: messages.default || messages,
33 },
34 });
35
36 // Re-export the real named exports for tests that need them
37 // eslint-disable-next-line global-require
38 const actual = jest.requireActual('@/i18n');
39 return {
40 __esModule: true,
41 default: mockI18n,
42 loadBaseLocaleMessages: actual.loadBaseLocaleMessages,
43 loadEnvLocaleMessages: actual.loadEnvLocaleMessages,
44 createI18nInstance: actual.createI18nInstance,
45 };
46});
47
48export const testI18n = createI18n({
49 legacy: false,
50 locale: 'en-US',
51 fallbackLocale: 'en-US',
52 globalInjection: true,
53 messages: {
54 'en-US': enUS,
55 en: enUS, // Alias for linked message resolution
56 },
57});
58
59// Provide default global mocks/stubs
60config.global.mocks = {
61 $t: (k) => k,
62 $route: { meta: { title: '' } },
63 $eventBus: {
64 on: () => {},
65 off: () => {},
66 emit: () => {},
67 $on: () => {},
68 $off: () => {},
69 $emit: () => {},
70 },
71};
72
73// Stubs with single root elements to properly inherit attributes like data-test-id
74config.global.stubs = {
75 'router-link': { template: '<a><slot/></a>' },
76 'b-navbar': { template: '<nav><slot/></nav>' },
77 'b-navbar-brand': { template: '<div><slot/></div>' },
78 'b-navbar-nav': { template: '<div><slot/></div>' },
79 'b-dropdown': { template: '<div><slot/></div>' },
80 'b-dropdown-item': { template: '<div><slot/></div>' },
81 'b-nav': { template: '<ul class="nav mb-4 flex-column"><slot/></ul>' },
82 'b-nav-item': { template: '<li><slot/></li>' },
83 'b-collapse': { template: '<ul><slot/></ul>' },
84 'b-button': { template: '<button><slot/></button>' },
85 'b-input-group': { template: '<div class="input-group"><slot/></div>' },
86 'b-input-group-prepend': {
87 template: '<div class="input-group-prepend"><slot/></div>',
88 },
89 'b-input-group-text': {
90 template: '<span class="input-group-text"><slot/></span>',
91 },
92 'b-form-group': { template: '<div class="form-group mb-2"><slot/></div>' },
93 'b-form-input': { template: '<input class="form-control search-input" />' },
94 'b-form-checkbox': { template: '<div class="form-check"><slot/></div>' },
95 'b-form-radio': { template: '<div class="form-check"><slot/></div>' },
96 'b-form-select': { template: '<select><slot/></select>' },
97 'b-progress': { template: '<div class="progress"><slot/></div>' },
98 'b-progress-bar': { template: '<div class="progress-bar"></div>' },
99 'b-modal': { template: '<div class="modal"><slot/></div>' },
100 'b-tooltip': { template: '<div><slot/></div>' },
101};
102
103// Provide plugins - i18n for useI18n() support, and $root helpers
104config.global.plugins = [
105 testI18n,
106 {
107 install(app) {
108 // eslint-disable-next-line no-param-reassign
109 app.config.globalProperties.$root =
110 app.config.globalProperties.$root || {};
111 if (!app.config.globalProperties.$root.$on) {
112 app.config.globalProperties.$root.$on = () => {};
113 }
114 if (!app.config.globalProperties.$root.$emit) {
115 app.config.globalProperties.$root.$emit = () => {};
116 }
117 app.mixin({
118 beforeCreate() {
119 const r = this.$root;
120 if (r && !r.$on) r.$on = () => {};
121 if (r && !r.$emit) r.$emit = () => {};
122 },
123 });
124 },
125 },
126];
127
128// Stub bootstrap-vue directives
129config.global.directives = {
130 'b-tooltip': () => {},
131 'b-toggle': () => {},
132};