layout-store.client.spec.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // @vitest-environment jsdom
  2. /**
  3. * createLayoutStore unit account: init shape, the action write set (clamp
  4. * inside actions), and the absence of browser persistence. Uses the
  5. * test-sanctioned path: factory self-call + .create() gives the
  6. * real engine instance (same create path as production).
  7. */
  8. import { beforeEach, describe, expect, it } from 'vitest'
  9. import { createLayoutStore } from '@deepseek-ai/dsh-client-ui-layout/src/client/stores.ts'
  10. import {
  11. DETAILS_DEFAULT, DETAILS_MAX, DETAILS_MIN,
  12. SIDEBAR_DEFAULT, SIDEBAR_MAX, SIDEBAR_MIN,
  13. } from '@deepseek-ai/dsh-client-ui-layout/src/client/columns.ts'
  14. const PERSIST_KEY = 'dsh.layout.panels'
  15. beforeEach(() => { localStorage.clear() })
  16. describe('createLayoutStore', () => {
  17. it('initializes the sidebar at its default width, details closed, wide viewport assumed', () => {
  18. const { store } = createLayoutStore().create()
  19. expect(store.getSnapshot()).toEqual({ sidebar: SIDEBAR_DEFAULT, details: 0, narrow: false, narrowExpanded: false })
  20. })
  21. it('each create() is an independent instance (factory is not a singleton)', () => {
  22. const a = createLayoutStore().create()
  23. const b = createLayoutStore().create()
  24. a.actions.setSidebar(400)
  25. expect(b.store.getSnapshot().sidebar).toBe(SIDEBAR_DEFAULT)
  26. })
  27. it('setSidebar/setDetails clamp into the contract ranges', () => {
  28. const { store, actions } = createLayoutStore().create()
  29. actions.setSidebar(1)
  30. expect(store.getSnapshot().sidebar).toBe(SIDEBAR_MIN)
  31. actions.setSidebar(9999)
  32. expect(store.getSnapshot().sidebar).toBe(SIDEBAR_MAX)
  33. actions.setDetails(1)
  34. expect(store.getSnapshot().details).toBe(DETAILS_MIN)
  35. actions.setDetails(9999)
  36. expect(store.getSnapshot().details).toBe(DETAILS_MAX)
  37. })
  38. it('toggleSidebar flips closed <-> contract default (drag width forgotten)', () => {
  39. const { store, actions } = createLayoutStore().create()
  40. actions.setSidebar(400)
  41. actions.toggleSidebar()
  42. expect(store.getSnapshot().sidebar).toBe(0)
  43. actions.toggleSidebar()
  44. expect(store.getSnapshot().sidebar).toBe(SIDEBAR_DEFAULT)
  45. })
  46. it('narrow toggleSidebar flips only the re-expand override; the width preference survives', () => {
  47. const { store, actions } = createLayoutStore().create()
  48. actions.setSidebar(400)
  49. actions.setNarrow(true)
  50. actions.toggleSidebar()
  51. expect(store.getSnapshot()).toEqual({ sidebar: 400, details: 0, narrow: true, narrowExpanded: true })
  52. actions.toggleSidebar()
  53. expect(store.getSnapshot().narrowExpanded).toBe(false)
  54. expect(store.getSnapshot().sidebar).toBe(400)
  55. })
  56. it('crossing the breakpoint drops the override; a same-value setNarrow keeps it', () => {
  57. const { store, actions } = createLayoutStore().create()
  58. actions.setNarrow(true)
  59. actions.toggleSidebar()
  60. expect(store.getSnapshot().narrowExpanded).toBe(true)
  61. actions.setNarrow(true)
  62. expect(store.getSnapshot().narrowExpanded).toBe(true)
  63. actions.setNarrow(false)
  64. expect(store.getSnapshot()).toMatchObject({ narrow: false, narrowExpanded: false })
  65. actions.setNarrow(true)
  66. expect(store.getSnapshot().narrowExpanded).toBe(false)
  67. })
  68. it('openDetails uses the contract default, preserves an open width, and closeDetails zeroes', () => {
  69. const { store, actions } = createLayoutStore().create()
  70. actions.openDetails()
  71. expect(store.getSnapshot().details).toBe(DETAILS_DEFAULT)
  72. actions.setDetails(500)
  73. actions.openDetails()
  74. expect(store.getSnapshot().details).toBe(500)
  75. actions.closeDetails()
  76. expect(store.getSnapshot().details).toBe(0)
  77. })
  78. it('does not persist panel geometry', () => {
  79. const first = createLayoutStore().create()
  80. first.actions.setSidebar(400)
  81. first.actions.openDetails()
  82. first.actions.setDetails(500)
  83. expect(localStorage.getItem(PERSIST_KEY)).toBeNull()
  84. const second = createLayoutStore().create()
  85. expect(second.store.getSnapshot()).toEqual({
  86. sidebar: SIDEBAR_DEFAULT,
  87. details: 0,
  88. narrow: false,
  89. narrowExpanded: false,
  90. })
  91. })
  92. })