Skip to content

Commit 57465e6

Browse files
test(core): stub BillingNavComputeGaugeComponent in core.navigation unit tests (#4180)
* test(core): stub BillingNavComputeGaugeComponent in core.navigation unit tests Closes #4129. The real gauge auto-fetches via the billing store on mount, silently coupling navigation unit tests to billing infrastructure. Adding a default stub in mountNav() decouples the suites and enables a positive-path test (meterMode=true → gauge slot renders). * test(core): defensive intra-test reset of authStoreState (Copilot review nits) - Reword comment: 'mutable hoisted ref' → 'mutable hoisted object' (it's a plain hoisted object, not a Vue ref). Avoids future-edit confusion. - Wrap the meterMode=true test's authStoreState mutation in try/finally so the override cannot leak into subsequent tests/describes even if mountNav() throws. Next describes' beforeEach already reset state, but defensive intra-test reset is cleaner and resilient to test reordering.
1 parent d1403ff commit 57465e6

1 file changed

Lines changed: 35 additions & 8 deletions

File tree

src/modules/core/tests/core.navigation.component.unit.tests.js

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,17 @@ vi.mock('vuetify', async (importOriginal) => {
1818
};
1919
});
2020

21-
// Mock auth store — report logged in + org so the drawer actually renders
21+
// Mock auth store — report logged in + org so the drawer actually renders.
22+
// authStoreState is a mutable hoisted object so individual tests can override
23+
// serverConfig (e.g. to enable billing.meterMode) without re-mocking.
24+
const authStoreState = vi.hoisted(() => ({
25+
isLoggedIn: true,
26+
user: { currentOrganization: { _id: 'org1', name: 'Org' } },
27+
serverConfig: { organizations: { enabled: false } },
28+
signout: vi.fn(),
29+
}));
2230
vi.mock('../../auth/stores/auth.store', () => ({
23-
useAuthStore: () => ({
24-
isLoggedIn: true,
25-
user: { currentOrganization: { _id: 'org1', name: 'Org' } },
26-
serverConfig: { organizations: { enabled: false } },
27-
signout: vi.fn(),
28-
}),
31+
useAuthStore: () => authStoreState,
2932
}));
3033

3134
// Mock core store — we inject our own nav + navBottom arrays per test
@@ -64,10 +67,11 @@ const makeConfig = (overrides = {}) => ({
6467
* @param {Object} opts
6568
* @param {Array} opts.nav - items for the main nav list
6669
* @param {Array} opts.navBottom - items for the bottom nav list
70+
* @param {Object} [opts.stubsOverride] - additional stubs merged after defaults (e.g. to replace the gauge stub)
6771
* @param {Object} [opts.configOverrides] - partial config overrides (e.g. { app: { logoFile: '/logo.svg' } })
6872
* @returns {import('@vue/test-utils').VueWrapper}
6973
*/
70-
const mountNav = ({ nav = [], navBottom = [], configOverrides = {} } = {}) => {
74+
const mountNav = ({ nav = [], navBottom = [], stubsOverride = {}, configOverrides = {} } = {}) => {
7175
coreStoreState.nav = nav;
7276
coreStoreState.navBottom = navBottom;
7377
const vuetify = createVuetify({ components, directives });
@@ -82,6 +86,9 @@ const mountNav = ({ nav = [], navBottom = [], configOverrides = {} } = {}) => {
8286
'router-link': { template: '<a><slot /></a>' },
8387
// Stub v-navigation-drawer to a plain wrapper — the real one needs a <v-layout> ancestor
8488
'v-navigation-drawer': { template: '<div class="v-navigation-drawer"><slot /><slot name="append" /></div>' },
89+
// Stub the gauge to prevent it from auto-fetching billing data on mount
90+
BillingNavComputeGaugeComponent: { template: '<div class="stub-billing-nav-compute-gauge" />' },
91+
...stubsOverride,
8592
},
8693
},
8794
});
@@ -92,6 +99,7 @@ describe('core.navigation.component — navItemBind', () => {
9299
setActivePinia(createPinia());
93100
coreStoreState.nav = [];
94101
coreStoreState.navBottom = [];
102+
authStoreState.serverConfig = { organizations: { enabled: false } };
95103
});
96104

97105
it('returns { to } for a regular internal route', () => {
@@ -143,6 +151,7 @@ describe('core.navigation.component — navItemBind', () => {
143151
describe('core.navigation.component — template rendering', () => {
144152
beforeEach(() => {
145153
setActivePinia(createPinia());
154+
authStoreState.serverConfig = { organizations: { enabled: false } };
146155
});
147156

148157
it('renders a regular nav item as a router-link (no href attribute)', () => {
@@ -231,6 +240,8 @@ describe('core.navigation.component — compute gauge slot', () => {
231240
setActivePinia(createPinia());
232241
coreStoreState.nav = [];
233242
coreStoreState.navBottom = [];
243+
// Reset auth state to default (no billing) so tests are independent
244+
authStoreState.serverConfig = { organizations: { enabled: false } };
234245
});
235246

236247
it('meterMode computed returns false when serverConfig has no billing.meterMode', () => {
@@ -294,11 +305,27 @@ describe('core.navigation.component — compute gauge slot', () => {
294305
expect(signOutPos).toBeGreaterThan(accountPos);
295306
expect(signOutPos).toBeGreaterThan(settingsPos);
296307
});
308+
309+
it('renders stubbed BillingNavComputeGaugeComponent when meterMode is true', () => {
310+
// Override auth state so meterMode resolves to true for this test only.
311+
// The try/finally below resets it immediately so the override cannot leak
312+
// into subsequent tests/describes even if mountNav() ever throws.
313+
const defaultServerConfig = { organizations: { enabled: false } };
314+
authStoreState.serverConfig = { organizations: { enabled: false }, billing: { meterMode: true } };
315+
try {
316+
const wrapper = mountNav();
317+
expect(wrapper.vm.meterMode).toBe(true);
318+
expect(wrapper.find('.stub-billing-nav-compute-gauge').exists()).toBe(true);
319+
} finally {
320+
authStoreState.serverConfig = defaultServerConfig;
321+
}
322+
});
297323
});
298324

299325
describe('core.navigation.component — sidenav logo', () => {
300326
beforeEach(() => {
301327
setActivePinia(createPinia());
328+
authStoreState.serverConfig = { organizations: { enabled: false } };
302329
});
303330

304331
it('renders a v-img when config.app.logoFile is set', () => {

0 commit comments

Comments
 (0)