utils.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import { nextTick } from 'vue'
  3. import type { ViewerOptions } from '#shared/composables/useImageViewer.ts'
  4. import type { MockGraphQLInstance } from './mock-graphql-api'
  5. import type { Mock } from 'vitest'
  6. import type { Ref } from 'vue'
  7. const state = Symbol('test-state')
  8. interface TestState {
  9. imageViewerOptions: Ref<ViewerOptions>
  10. }
  11. export const getTestState = (): TestState => {
  12. return (globalThis as any)[state] || {}
  13. }
  14. export const setTestState = (newState: Partial<TestState>) => {
  15. ;(globalThis as any)[state] = {
  16. ...getTestState(),
  17. ...newState,
  18. }
  19. }
  20. export const waitForTimeout = async (milliseconds = 0) => {
  21. return new Promise((resolve) => {
  22. setTimeout(resolve, milliseconds)
  23. })
  24. }
  25. export const waitForNextTick = async (withTimeout = false) => {
  26. if (withTimeout) {
  27. await nextTick()
  28. return new Promise((resolve) => {
  29. setTimeout(resolve, 0)
  30. })
  31. }
  32. return nextTick()
  33. }
  34. export const waitUntil = async (
  35. condition: () => unknown,
  36. msThreshold = process.env.CI ? 30_000 : 1_000,
  37. ) => {
  38. // point stack trace to the place where "waitUntil" was called
  39. const err = new Error('Timeout')
  40. Error.captureStackTrace(err, waitUntil)
  41. return new Promise<void>((resolve, reject) => {
  42. const start = Date.now()
  43. const max = start + msThreshold
  44. const interval = setInterval(() => {
  45. if (condition()) {
  46. clearInterval(interval)
  47. resolve()
  48. }
  49. if (max < Date.now()) {
  50. clearInterval(interval)
  51. reject(err)
  52. }
  53. }, 30)
  54. })
  55. }
  56. export const waitUntilApisResolved = (...mockApis: MockGraphQLInstance[]) => {
  57. return waitUntil(() => mockApis.every((mock) => mock.calls.resolve))
  58. }
  59. export const waitUntilSpyCalled = (spy: Mock) => {
  60. return waitUntil(() => spy.mock.calls.length > 0)
  61. }
  62. // The apollo cache always asks for a field, even if it's marked as optional
  63. // this function returns a proxy that will return "null" on properties not defined
  64. // in the initial object.
  65. export const nullableMock = <T extends object>(obj: T): T => {
  66. const skipProperties = new Set(['_id', 'id', Symbol.toStringTag])
  67. return new Proxy(obj, {
  68. get(target, prop, receiver) {
  69. if (!Reflect.has(target, prop) && !skipProperties.has(prop)) {
  70. return null
  71. }
  72. const value = Reflect.get(target, prop, receiver)
  73. if (
  74. Array.isArray(value) &&
  75. value.every((item) => typeof item === 'object' && item !== null)
  76. ) {
  77. return value.map(nullableMock)
  78. }
  79. if (typeof value === 'object' && value !== null) {
  80. return nullableMock(value)
  81. }
  82. return value
  83. },
  84. })
  85. }
  86. export const dataURItoBlob = (dataURI: string) => {
  87. const byteString = atob(dataURI.split(',')[1])
  88. const mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
  89. const ab = new ArrayBuffer(byteString.length)
  90. const ia = new Uint8Array(ab)
  91. for (let i = 0; i < byteString.length; i += 1) {
  92. ia[i] = byteString.charCodeAt(i)
  93. }
  94. return new Blob([ab], { type: mimeString })
  95. }