settings.spec.ts 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. import { test, expect, settingsKey } from "../fixtures"
  2. import { closeDialog, openSettings } from "../actions"
  3. import {
  4. settingsColorSchemeSelector,
  5. settingsFontSelector,
  6. settingsLanguageSelectSelector,
  7. settingsNotificationsAgentSelector,
  8. settingsNotificationsErrorsSelector,
  9. settingsNotificationsPermissionsSelector,
  10. settingsReleaseNotesSelector,
  11. settingsSoundsAgentSelector,
  12. settingsThemeSelector,
  13. settingsUpdatesStartupSelector,
  14. } from "../selectors"
  15. test("smoke settings dialog opens, switches tabs, closes", async ({ page, gotoSession }) => {
  16. await gotoSession()
  17. const dialog = await openSettings(page)
  18. await dialog.getByRole("tab", { name: "Shortcuts" }).click()
  19. await expect(dialog.getByRole("button", { name: "Reset to defaults" })).toBeVisible()
  20. await expect(dialog.getByPlaceholder("Search shortcuts")).toBeVisible()
  21. await closeDialog(page, dialog)
  22. })
  23. test("changing language updates settings labels", async ({ page, gotoSession }) => {
  24. await page.addInitScript(() => {
  25. localStorage.setItem("opencode.global.dat:language", JSON.stringify({ locale: "en" }))
  26. })
  27. await gotoSession()
  28. const dialog = await openSettings(page)
  29. const heading = dialog.getByRole("heading", { level: 2 })
  30. await expect(heading).toHaveText("General")
  31. const select = dialog.locator(settingsLanguageSelectSelector)
  32. await expect(select).toBeVisible()
  33. await select.locator('[data-slot="select-select-trigger"]').click()
  34. await page.locator('[data-slot="select-select-item"]').filter({ hasText: "Deutsch" }).click()
  35. await expect(heading).toHaveText("Allgemein")
  36. await select.locator('[data-slot="select-select-trigger"]').click()
  37. await page.locator('[data-slot="select-select-item"]').filter({ hasText: "English" }).click()
  38. await expect(heading).toHaveText("General")
  39. })
  40. test("changing color scheme persists in localStorage", async ({ page, gotoSession }) => {
  41. await gotoSession()
  42. const dialog = await openSettings(page)
  43. const select = dialog.locator(settingsColorSchemeSelector)
  44. await expect(select).toBeVisible()
  45. await select.locator('[data-slot="select-select-trigger"]').click()
  46. await page.locator('[data-slot="select-select-item"]').filter({ hasText: "Dark" }).click()
  47. const colorScheme = await page.evaluate(() => {
  48. return document.documentElement.getAttribute("data-color-scheme")
  49. })
  50. expect(colorScheme).toBe("dark")
  51. await select.locator('[data-slot="select-select-trigger"]').click()
  52. await page.locator('[data-slot="select-select-item"]').filter({ hasText: "Light" }).click()
  53. const lightColorScheme = await page.evaluate(() => {
  54. return document.documentElement.getAttribute("data-color-scheme")
  55. })
  56. expect(lightColorScheme).toBe("light")
  57. })
  58. test("changing theme persists in localStorage", async ({ page, gotoSession }) => {
  59. await gotoSession()
  60. const dialog = await openSettings(page)
  61. const select = dialog.locator(settingsThemeSelector)
  62. await expect(select).toBeVisible()
  63. await select.locator('[data-slot="select-select-trigger"]').click()
  64. const items = page.locator('[data-slot="select-select-item"]')
  65. const count = await items.count()
  66. expect(count).toBeGreaterThan(1)
  67. const firstTheme = await items.nth(1).locator('[data-slot="select-select-item-label"]').textContent()
  68. expect(firstTheme).toBeTruthy()
  69. await items.nth(1).click()
  70. await page.keyboard.press("Escape")
  71. const storedThemeId = await page.evaluate(() => {
  72. return localStorage.getItem("opencode-theme-id")
  73. })
  74. expect(storedThemeId).not.toBeNull()
  75. expect(storedThemeId).not.toBe("oc-1")
  76. const dataTheme = await page.evaluate(() => {
  77. return document.documentElement.getAttribute("data-theme")
  78. })
  79. expect(dataTheme).toBe(storedThemeId)
  80. })
  81. test("changing font persists in localStorage and updates CSS variable", async ({ page, gotoSession }) => {
  82. await gotoSession()
  83. const dialog = await openSettings(page)
  84. const select = dialog.locator(settingsFontSelector)
  85. await expect(select).toBeVisible()
  86. const initialFontFamily = await page.evaluate(() => {
  87. return getComputedStyle(document.documentElement).getPropertyValue("--font-family-mono")
  88. })
  89. expect(initialFontFamily).toContain("IBM Plex Mono")
  90. await select.locator('[data-slot="select-select-trigger"]').click()
  91. const items = page.locator('[data-slot="select-select-item"]')
  92. await items.nth(2).click()
  93. await page.waitForTimeout(100)
  94. const stored = await page.evaluate((key) => {
  95. const raw = localStorage.getItem(key)
  96. return raw ? JSON.parse(raw) : null
  97. }, settingsKey)
  98. expect(stored?.appearance?.font).not.toBe("ibm-plex-mono")
  99. const newFontFamily = await page.evaluate(() => {
  100. return getComputedStyle(document.documentElement).getPropertyValue("--font-family-mono")
  101. })
  102. expect(newFontFamily).not.toBe(initialFontFamily)
  103. })
  104. test("toggling notification agent switch updates localStorage", async ({ page, gotoSession }) => {
  105. await gotoSession()
  106. const dialog = await openSettings(page)
  107. const switchContainer = dialog.locator(settingsNotificationsAgentSelector)
  108. await expect(switchContainer).toBeVisible()
  109. const toggleInput = switchContainer.locator('[data-slot="switch-input"]')
  110. const initialState = await toggleInput.evaluate((el: HTMLInputElement) => el.checked)
  111. expect(initialState).toBe(true)
  112. await switchContainer.locator('[data-slot="switch-control"]').click()
  113. await page.waitForTimeout(100)
  114. const newState = await toggleInput.evaluate((el: HTMLInputElement) => el.checked)
  115. expect(newState).toBe(false)
  116. const stored = await page.evaluate((key) => {
  117. const raw = localStorage.getItem(key)
  118. return raw ? JSON.parse(raw) : null
  119. }, settingsKey)
  120. expect(stored?.notifications?.agent).toBe(false)
  121. })
  122. test("toggling notification permissions switch updates localStorage", async ({ page, gotoSession }) => {
  123. await gotoSession()
  124. const dialog = await openSettings(page)
  125. const switchContainer = dialog.locator(settingsNotificationsPermissionsSelector)
  126. await expect(switchContainer).toBeVisible()
  127. const toggleInput = switchContainer.locator('[data-slot="switch-input"]')
  128. const initialState = await toggleInput.evaluate((el: HTMLInputElement) => el.checked)
  129. expect(initialState).toBe(true)
  130. await switchContainer.locator('[data-slot="switch-control"]').click()
  131. await page.waitForTimeout(100)
  132. const newState = await toggleInput.evaluate((el: HTMLInputElement) => el.checked)
  133. expect(newState).toBe(false)
  134. const stored = await page.evaluate((key) => {
  135. const raw = localStorage.getItem(key)
  136. return raw ? JSON.parse(raw) : null
  137. }, settingsKey)
  138. expect(stored?.notifications?.permissions).toBe(false)
  139. })
  140. test("toggling notification errors switch updates localStorage", async ({ page, gotoSession }) => {
  141. await gotoSession()
  142. const dialog = await openSettings(page)
  143. const switchContainer = dialog.locator(settingsNotificationsErrorsSelector)
  144. await expect(switchContainer).toBeVisible()
  145. const toggleInput = switchContainer.locator('[data-slot="switch-input"]')
  146. const initialState = await toggleInput.evaluate((el: HTMLInputElement) => el.checked)
  147. expect(initialState).toBe(false)
  148. await switchContainer.locator('[data-slot="switch-control"]').click()
  149. await page.waitForTimeout(100)
  150. const newState = await toggleInput.evaluate((el: HTMLInputElement) => el.checked)
  151. expect(newState).toBe(true)
  152. const stored = await page.evaluate((key) => {
  153. const raw = localStorage.getItem(key)
  154. return raw ? JSON.parse(raw) : null
  155. }, settingsKey)
  156. expect(stored?.notifications?.errors).toBe(true)
  157. })
  158. test("changing sound agent selection persists in localStorage", async ({ page, gotoSession }) => {
  159. await gotoSession()
  160. const dialog = await openSettings(page)
  161. const select = dialog.locator(settingsSoundsAgentSelector)
  162. await expect(select).toBeVisible()
  163. await select.locator('[data-slot="select-select-trigger"]').click()
  164. const items = page.locator('[data-slot="select-select-item"]')
  165. await items.nth(2).click()
  166. const stored = await page.evaluate((key) => {
  167. const raw = localStorage.getItem(key)
  168. return raw ? JSON.parse(raw) : null
  169. }, settingsKey)
  170. expect(stored?.sounds?.agent).not.toBe("staplebops-01")
  171. })
  172. test("toggling updates startup switch updates localStorage", async ({ page, gotoSession }) => {
  173. await gotoSession()
  174. const dialog = await openSettings(page)
  175. const switchContainer = dialog.locator(settingsUpdatesStartupSelector)
  176. await expect(switchContainer).toBeVisible()
  177. const toggleInput = switchContainer.locator('[data-slot="switch-input"]')
  178. const isDisabled = await toggleInput.evaluate((el: HTMLInputElement) => el.disabled)
  179. if (isDisabled) {
  180. test.skip()
  181. return
  182. }
  183. const initialState = await toggleInput.evaluate((el: HTMLInputElement) => el.checked)
  184. expect(initialState).toBe(true)
  185. await switchContainer.locator('[data-slot="switch-control"]').click()
  186. await page.waitForTimeout(100)
  187. const newState = await toggleInput.evaluate((el: HTMLInputElement) => el.checked)
  188. expect(newState).toBe(false)
  189. const stored = await page.evaluate((key) => {
  190. const raw = localStorage.getItem(key)
  191. return raw ? JSON.parse(raw) : null
  192. }, settingsKey)
  193. expect(stored?.updates?.startup).toBe(false)
  194. })
  195. test("toggling release notes switch updates localStorage", async ({ page, gotoSession }) => {
  196. await gotoSession()
  197. const dialog = await openSettings(page)
  198. const switchContainer = dialog.locator(settingsReleaseNotesSelector)
  199. await expect(switchContainer).toBeVisible()
  200. const toggleInput = switchContainer.locator('[data-slot="switch-input"]')
  201. const initialState = await toggleInput.evaluate((el: HTMLInputElement) => el.checked)
  202. expect(initialState).toBe(true)
  203. await switchContainer.locator('[data-slot="switch-control"]').click()
  204. await page.waitForTimeout(100)
  205. const newState = await toggleInput.evaluate((el: HTMLInputElement) => el.checked)
  206. expect(newState).toBe(false)
  207. const stored = await page.evaluate((key) => {
  208. const raw = localStorage.getItem(key)
  209. return raw ? JSON.parse(raw) : null
  210. }, settingsKey)
  211. expect(stored?.general?.releaseNotes).toBe(false)
  212. })