云
2 天以前 1b57bc924e68db3873bf24deee91a75fa014ea96
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const ts = require('typescript');
const vue = require('vue');
const pinia = require('pinia');
const root = path.join(__dirname, '..');
function load(file, mocks = {}) {
  const source = fs.readFileSync(path.join(root, file), 'utf8')
    .replaceAll('import.meta.hot', 'undefined')
    .replaceAll('import.meta.env', '({ DEV: true })');
  const js = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022, esModuleInterop: true } }).outputText;
  const module = { exports: {} };
  new Function('require', 'module', 'exports', 'localStorage', js)((id) => {
    if (id in mocks) return mocks[id];
    if (id === 'vue') return vue;
    if (id === 'pinia') return pinia;
    throw new Error(`Unexpected import ${id} in ${file}`);
  }, module, module.exports, {});
  return module.exports;
}
const constants = load('src/views/im/utils/constants.ts');
async function scenario(apiFails, resetFails, redirect) {
  const app = vue.createApp({});
  const setup = load('src/packages/stores/src/setup.ts', {
    'secure-ls': class {}, 'pinia-plugin-persistedstate': { createPersistedState: () => () => {} },
  });
  const instance = await setup.initStores(app, { namespace: 'logout-test' });
  pinia.setActivePinia(instance);
  const company = load('src/store/company-config.ts', {
    '#/api/system/company-config': {}, '#/utils/company-config': {},
  }).useCompanyConfigStore();
  company.companyName = 'Test company';
  company.logoUrl = '/test-logo.png';
  company.loginTitle = 'Test';
  company.loginDescription = 'Test description';
  const ui = load('src/views/im/home/store/uiStore.ts', { '../../utils/constants': constants }).useImUiStore();
  ui.userInfoCard.show = true;
  ui.userInfoCard.user = { id: 1 };
  ui.contextMenu.items = [{ key: 'private', name: 'private' }];
  ui.contextMenu.onSelect = () => {};
  ui.nextUnreadJumpNonce = 3;
  const rtc = load('src/views/im/home/store/rtcStore.ts', {
    '../../utils/constants': constants, '#/views/im/utils/auth': {}, './friendStore': {},
    './groupStore': { useGroupStore: () => ({ markGroupActiveCallLoaded: () => {} }) },
  }).useRtcStore();
  rtc.stage = constants.ImRtcCallStage.RUNNING;
  rtc.call = { id: 1 };
  rtc.setGroupCall({ groupId: 1, room: 'test', joinedUserIds: [1], inviteeIds: [] });
  const accessModule = load('src/packages/stores/src/modules/access.ts');
  const userModule = load('src/packages/stores/src/modules/user.ts');
  const access = accessModule.useAccessStore();
  access.setAccessToken('old-access');
  access.setRefreshToken('old-refresh');
  access.setIsAccessChecked(true);
  access.setLoginExpired(true);
  userModule.useUserStore().setUserInfo({ userId: 1 });
  if (resetFails) pinia.defineStore('broken-business-store', () => ({ count: vue.ref(1) }))();
  const navigations = [];
  const apiCalls = [];
  const auth = load('src/store/auth.ts', {
    'vue-router': { useRouter: () => ({ currentRoute: vue.ref({ fullPath: '/analytics?tab=1' }), replace: async (to) => navigations.push(to) }) },
    '@vben/constants': { LOGIN_PATH: '/auth/login' }, '@vben/preferences': {},
    '@vben/stores': { ...setup, ...accessModule, ...userModule }, 'ant-design-vue': {}, '#/locales': {},
    '#/api': { logoutApi: async (token) => { apiCalls.push(token); if (apiFails) throw new Error('offline'); } },
  }).useAuthStore();
  const errors = [];
  const originalError = console.error;
  console.error = (...args) => errors.push(args);
  try { await auth.logout(redirect); } finally { console.error = originalError; }
  assert.deepEqual(apiCalls, ['old-access']);
  assert.equal(navigations.length, 1);
  assert.deepEqual(navigations[0], { path: '/auth/login', query: redirect ? { redirect: encodeURIComponent('/analytics?tab=1') } : {} });
  assert.equal(access.accessToken, null);
  assert.equal(access.refreshToken, null);
  assert.equal(access.isAccessChecked, false);
  assert.equal(access.loginExpired, false);
  assert.equal(userModule.useUserStore().userInfo, null);
  assert.equal(company.companyName, '');
  assert.equal(company.logoUrl, '');
  assert.equal(company.loginTitle, '');
  assert.equal(company.loginDescription, '');
  assert.equal(ui.userInfoCard.show, false);
  assert.equal(ui.userInfoCard.user, null);
  assert.deepEqual(ui.contextMenu.items, []);
  assert.equal(ui.contextMenu.onSelect, null);
  assert.equal(ui.nextUnreadJumpNonce, 0);
  assert.equal(rtc.stage, constants.ImRtcCallStage.IDLE);
  assert.equal(rtc.call, null);
  assert.equal(rtc.getGroupCall(1), undefined);
  assert.equal(errors.length, resetFails ? 1 : 0);
}
(async () => {
  await scenario(false, false, false);
  await scenario(true, false, true);
  await scenario(false, true, false);
  await scenario(true, true, false);
  console.log('PASS: logout redirects and clears session with API success/failure and store reset failures; company and IM stores reset');
})();