client-build-environment.client.spec.ts 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. import { readFileSync } from 'node:fs'
  2. import { resolve } from 'node:path'
  3. import yaml from 'js-yaml'
  4. import { afterEach, describe, expect, it, vi } from 'vitest'
  5. import { clientBuildEnvironmentDefines } from './client-build-environment.ts'
  6. import { clientBundle } from '../packages/client/tsdown.client.ts'
  7. const root = resolve(import.meta.dirname, '..')
  8. const PROBE_NAME = 'DSH_CLIENT_BUILD_TEST'
  9. const PROBE_KEY = `process.env.${PROBE_NAME}`
  10. const originalProbe = process.env[PROBE_NAME]
  11. const dshBuildWorkflows = [
  12. 'build-exe-for-python-sdk.yml',
  13. 'ci.yml',
  14. 'e2b-e2e.yml',
  15. 'e2e.yml',
  16. 'release.yml',
  17. 'sandbox.yml',
  18. ]
  19. afterEach(() => {
  20. if (originalProbe === undefined) Reflect.deleteProperty(process.env, PROBE_NAME)
  21. else process.env[PROBE_NAME] = originalProbe
  22. vi.resetModules()
  23. })
  24. describe('client build environment', () => {
  25. it('defines only public client values over a non-enumerable fallback', () => {
  26. expect(clientBuildEnvironmentDefines({
  27. PATH: '/bin',
  28. DSH_TEST_API_KEY: 'secret',
  29. DSH_CLIENT_VARIANT: 'quoted "value"',
  30. DSH_CLIENT_EMPTY: '',
  31. DSH_CLIENT_UNSET: undefined,
  32. })).toEqual({
  33. 'process.env': '{}',
  34. 'process.env.DSH_CLIENT_EMPTY': '""',
  35. 'process.env.DSH_CLIENT_VARIANT': '"quoted \\"value\\""',
  36. })
  37. })
  38. it('feeds the same build-process value to dynamic tsdown bundles and the Vite shell', async () => {
  39. process.env[PROBE_NAME] = 'shared-value'
  40. const configs = clientBundle('@deepseek-ai/dsh-client-ui-sidebar', [
  41. 'lib/types/index.js',
  42. 'lib/types/invariant.js',
  43. ])({ env: { DSH_BUILD_FACE: 'client' } })
  44. if (!Array.isArray(configs)) throw new TypeError('client bundle config must be an array')
  45. const dynamic = configs.find(config => config.name === '@deepseek-ai/dsh-client-ui-sidebar/client')
  46. expect(dynamic?.define).toMatchObject({
  47. 'process.env': '{}',
  48. [PROBE_KEY]: '"shared-value"',
  49. })
  50. const viteConfigPath = '../apps/web/vite.config.ts'
  51. const viteModule: unknown = await import(viteConfigPath)
  52. if (typeof viteModule !== 'object' || viteModule === null) {
  53. throw new TypeError('web Vite config module must be an object')
  54. }
  55. const viteConfig: unknown = Reflect.get(viteModule, 'default')
  56. if (typeof viteConfig === 'function') throw new TypeError('web Vite config must be an object')
  57. if (typeof viteConfig !== 'object' || viteConfig === null) {
  58. throw new TypeError('web Vite config must be an object')
  59. }
  60. expect(Reflect.get(viteConfig, 'define')).toMatchObject({
  61. 'process.env': '{}',
  62. [PROBE_KEY]: '"shared-value"',
  63. })
  64. })
  65. it('sets the official client build variant in DSH artifact build workflows', () => {
  66. for (const name of dshBuildWorkflows) {
  67. const path = `.github/workflows/${name}`
  68. const document: unknown = yaml.load(readFileSync(resolve(root, path), 'utf8'))
  69. if (typeof document !== 'object' || document === null || Array.isArray(document)) {
  70. throw new TypeError(`${path} must contain a workflow object`)
  71. }
  72. const environment: unknown = Reflect.get(document, 'env')
  73. expect(environment, path).toMatchObject({ DSH_CLIENT_BRAND: 'official' })
  74. }
  75. })
  76. })