1
0

pinia-store-synthesizer.test.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. /**
  2. * Pinia `useStore().action()` dispatch bridge.
  3. *
  4. * A Pinia store factory `export const useXStore = defineStore(...)` exposes its
  5. * actions as methods on the store instance; a consumer does `const s = useXStore()`
  6. * then `s.action()`. That method-on-instance call has no static edge to the action
  7. * (which lives in the store module). This bridges consumer → action by binding the
  8. * store var to its factory's file and resolving `s.method()` to a function node IN
  9. * THAT FILE — so it covers both the options and setup store forms, stays precise
  10. * (a Pinia built-in like `$patch`, or an unrelated same-named method, resolves to
  11. * nothing), and fires only when a `defineStore` factory actually exists.
  12. */
  13. import { describe, it, expect, beforeEach, afterEach } from 'vitest';
  14. import * as fs from 'node:fs';
  15. import * as path from 'node:path';
  16. import * as os from 'node:os';
  17. import { CodeGraph } from '../src';
  18. describe('pinia-store synthesizer', () => {
  19. let dir: string;
  20. beforeEach(() => { dir = fs.mkdtempSync(path.join(os.tmpdir(), 'pinia-store-')); });
  21. afterEach(() => { fs.rmSync(dir, { recursive: true, force: true }); });
  22. it('bridges `const s = useXStore(); s.action()` to the action, across options + setup forms', async () => {
  23. // Options-form store.
  24. fs.writeFileSync(
  25. path.join(dir, 'authStore.ts'),
  26. `import { defineStore } from 'pinia';
  27. export const useAuthStore = defineStore({
  28. id: 'auth',
  29. state: () => ({ token: '' }),
  30. actions: {
  31. async getMenu() { return loadMenu(); },
  32. setToken(t: string) { this.token = t; },
  33. },
  34. });
  35. `
  36. );
  37. // Setup-form store.
  38. fs.writeFileSync(
  39. path.join(dir, 'chatStore.ts'),
  40. `import { defineStore } from 'pinia';
  41. export const useChatStore = defineStore('chat', () => {
  42. const getList = async () => { return fetchList(); };
  43. return { getList };
  44. });
  45. `
  46. );
  47. // Consumer binds both stores and calls their actions (plus a Pinia built-in).
  48. fs.writeFileSync(
  49. path.join(dir, 'init.ts'),
  50. `import { useAuthStore } from './authStore';
  51. import { useChatStore } from './chatStore';
  52. export function init() {
  53. const authStore = useAuthStore();
  54. const chatStore = useChatStore();
  55. authStore.getMenu();
  56. authStore.setToken('x');
  57. authStore.$patch({}); // Pinia built-in — must not bridge
  58. chatStore.getList();
  59. }
  60. `
  61. );
  62. const cg = await CodeGraph.init(dir, { silent: true });
  63. await cg.indexAll();
  64. const db = (cg as any).db.db;
  65. const edges = db
  66. .prepare(
  67. `SELECT s.name source, t.name target, t.file_path tf
  68. FROM edges e JOIN nodes s ON s.id = e.source JOIN nodes t ON t.id = e.target
  69. WHERE json_extract(e.metadata,'$.synthesizedBy') = 'pinia-store'`
  70. )
  71. .all();
  72. const pairs = edges.map((r: any) => `${r.source}->${r.target}`).sort();
  73. // Exactly the three real actions, all from `init`.
  74. expect(pairs).toEqual(['init->getList', 'init->getMenu', 'init->setToken']);
  75. // Each target is the action in its own store file (cross-file, store-scoped).
  76. expect(edges.every((r: any) => /Store\.ts$/.test(r.tf))).toBe(true);
  77. // The Pinia built-in `$patch` produced no edge.
  78. expect(pairs.some((p: string) => p.includes('patch'))).toBe(false);
  79. cg.close?.();
  80. });
  81. it('produces nothing when there is no defineStore factory (not a Pinia store)', async () => {
  82. fs.writeFileSync(
  83. path.join(dir, 'thing.ts'),
  84. `function useThing() { return { run() { return 1; } }; }
  85. export function go() {
  86. const thing = useThing();
  87. thing.run();
  88. }
  89. `
  90. );
  91. const cg = await CodeGraph.init(dir, { silent: true });
  92. await cg.indexAll();
  93. const db = (cg as any).db.db;
  94. const c = db
  95. .prepare(`SELECT count(*) c FROM edges WHERE json_extract(metadata,'$.synthesizedBy') = 'pinia-store'`)
  96. .get().c;
  97. expect(c).toBe(0);
  98. cg.close?.();
  99. });
  100. });