quasar 2.15.3 → 2.15.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/dist/api/AppFullscreen.json +1 -1
  2. package/dist/icon-set/bootstrap-icons.umd.prod.js +1 -1
  3. package/dist/icon-set/eva-icons.umd.prod.js +1 -1
  4. package/dist/icon-set/fontawesome-v5-pro.umd.prod.js +1 -1
  5. package/dist/icon-set/fontawesome-v5.umd.prod.js +1 -1
  6. package/dist/icon-set/fontawesome-v6-pro.umd.prod.js +1 -1
  7. package/dist/icon-set/fontawesome-v6.umd.prod.js +1 -1
  8. package/dist/icon-set/ionicons-v4.umd.prod.js +1 -1
  9. package/dist/icon-set/line-awesome.umd.prod.js +1 -1
  10. package/dist/icon-set/material-icons-outlined.umd.prod.js +1 -1
  11. package/dist/icon-set/material-icons-round.umd.prod.js +1 -1
  12. package/dist/icon-set/material-icons-sharp.umd.prod.js +1 -1
  13. package/dist/icon-set/material-icons.umd.prod.js +1 -1
  14. package/dist/icon-set/material-symbols-outlined.umd.prod.js +1 -1
  15. package/dist/icon-set/material-symbols-rounded.umd.prod.js +1 -1
  16. package/dist/icon-set/material-symbols-sharp.umd.prod.js +1 -1
  17. package/dist/icon-set/mdi-v3.umd.prod.js +1 -1
  18. package/dist/icon-set/mdi-v4.umd.prod.js +1 -1
  19. package/dist/icon-set/mdi-v5.umd.prod.js +1 -1
  20. package/dist/icon-set/mdi-v6.umd.prod.js +1 -1
  21. package/dist/icon-set/mdi-v7.umd.prod.js +1 -1
  22. package/dist/icon-set/svg-bootstrap-icons.umd.prod.js +1 -1
  23. package/dist/icon-set/svg-eva-icons.umd.prod.js +1 -1
  24. package/dist/icon-set/svg-fontawesome-v5.umd.prod.js +1 -1
  25. package/dist/icon-set/svg-fontawesome-v6.umd.prod.js +1 -1
  26. package/dist/icon-set/svg-ionicons-v4.umd.prod.js +1 -1
  27. package/dist/icon-set/svg-ionicons-v5.umd.prod.js +1 -1
  28. package/dist/icon-set/svg-ionicons-v6.umd.prod.js +1 -1
  29. package/dist/icon-set/svg-line-awesome.umd.prod.js +1 -1
  30. package/dist/icon-set/svg-material-icons-outlined.umd.prod.js +1 -1
  31. package/dist/icon-set/svg-material-icons-round.umd.prod.js +1 -1
  32. package/dist/icon-set/svg-material-icons-sharp.umd.prod.js +1 -1
  33. package/dist/icon-set/svg-material-icons.umd.prod.js +1 -1
  34. package/dist/icon-set/svg-material-symbols-outlined.umd.prod.js +1 -1
  35. package/dist/icon-set/svg-material-symbols-rounded.umd.prod.js +1 -1
  36. package/dist/icon-set/svg-material-symbols-sharp.umd.prod.js +1 -1
  37. package/dist/icon-set/svg-mdi-v6.umd.prod.js +1 -1
  38. package/dist/icon-set/svg-mdi-v7.umd.prod.js +1 -1
  39. package/dist/icon-set/svg-themify.umd.prod.js +1 -1
  40. package/dist/icon-set/themify.umd.prod.js +1 -1
  41. package/dist/lang/ar-TN.umd.prod.js +1 -1
  42. package/dist/lang/ar.umd.prod.js +1 -1
  43. package/dist/lang/az-Latn.umd.prod.js +1 -1
  44. package/dist/lang/bg.umd.prod.js +1 -1
  45. package/dist/lang/bn.umd.prod.js +1 -1
  46. package/dist/lang/ca.umd.prod.js +1 -1
  47. package/dist/lang/cs.umd.prod.js +1 -1
  48. package/dist/lang/da.umd.prod.js +1 -1
  49. package/dist/lang/de-CH.umd.prod.js +1 -1
  50. package/dist/lang/de-DE.umd.prod.js +1 -1
  51. package/dist/lang/de.umd.prod.js +1 -1
  52. package/dist/lang/el.umd.prod.js +1 -1
  53. package/dist/lang/en-GB.umd.prod.js +1 -1
  54. package/dist/lang/en-US.umd.prod.js +1 -1
  55. package/dist/lang/eo.umd.prod.js +1 -1
  56. package/dist/lang/es.umd.prod.js +1 -1
  57. package/dist/lang/et.umd.prod.js +1 -1
  58. package/dist/lang/eu.umd.prod.js +1 -1
  59. package/dist/lang/fa-IR.umd.prod.js +1 -1
  60. package/dist/lang/fa.umd.prod.js +1 -1
  61. package/dist/lang/fi.umd.prod.js +1 -1
  62. package/dist/lang/fr.umd.prod.js +1 -1
  63. package/dist/lang/gn.umd.prod.js +1 -1
  64. package/dist/lang/he.umd.prod.js +1 -1
  65. package/dist/lang/hi.umd.prod.js +1 -1
  66. package/dist/lang/hr.umd.prod.js +1 -1
  67. package/dist/lang/hu.umd.prod.js +1 -1
  68. package/dist/lang/id.umd.prod.js +1 -1
  69. package/dist/lang/is.umd.prod.js +1 -1
  70. package/dist/lang/it.umd.prod.js +1 -1
  71. package/dist/lang/ja.umd.prod.js +1 -1
  72. package/dist/lang/kk.umd.prod.js +1 -1
  73. package/dist/lang/km.umd.prod.js +1 -1
  74. package/dist/lang/ko-KR.umd.prod.js +1 -1
  75. package/dist/lang/kur-CKB.umd.prod.js +1 -1
  76. package/dist/lang/lt.umd.prod.js +1 -1
  77. package/dist/lang/lu.umd.prod.js +1 -1
  78. package/dist/lang/lv.umd.prod.js +1 -1
  79. package/dist/lang/mk.umd.prod.js +1 -1
  80. package/dist/lang/ml.umd.prod.js +1 -1
  81. package/dist/lang/mm.umd.prod.js +1 -1
  82. package/dist/lang/ms-MY.umd.prod.js +1 -1
  83. package/dist/lang/ms.umd.prod.js +1 -1
  84. package/dist/lang/my.umd.prod.js +1 -1
  85. package/dist/lang/nb-NO.umd.prod.js +1 -1
  86. package/dist/lang/nl.umd.prod.js +1 -1
  87. package/dist/lang/pl.umd.prod.js +1 -1
  88. package/dist/lang/pt-BR.umd.prod.js +1 -1
  89. package/dist/lang/pt.umd.prod.js +1 -1
  90. package/dist/lang/ro.umd.prod.js +1 -1
  91. package/dist/lang/ru.umd.prod.js +1 -1
  92. package/dist/lang/sk.umd.prod.js +1 -1
  93. package/dist/lang/sl.umd.prod.js +1 -1
  94. package/dist/lang/sm.umd.prod.js +1 -1
  95. package/dist/lang/sr-CYR.umd.prod.js +1 -1
  96. package/dist/lang/sr.umd.prod.js +1 -1
  97. package/dist/lang/sv.umd.prod.js +1 -1
  98. package/dist/lang/ta.umd.prod.js +1 -1
  99. package/dist/lang/th.umd.prod.js +1 -1
  100. package/dist/lang/tl.umd.prod.js +1 -1
  101. package/dist/lang/tr.umd.prod.js +1 -1
  102. package/dist/lang/ug.umd.prod.js +1 -1
  103. package/dist/lang/uk.umd.prod.js +1 -1
  104. package/dist/lang/uz-Cyrl.umd.prod.js +1 -1
  105. package/dist/lang/uz-Latn.umd.prod.js +1 -1
  106. package/dist/lang/vi.umd.prod.js +1 -1
  107. package/dist/lang/zh-CN.umd.prod.js +1 -1
  108. package/dist/lang/zh-TW.umd.prod.js +1 -1
  109. package/dist/quasar.cjs.prod.js +3 -3
  110. package/dist/quasar.esm.js +8 -7
  111. package/dist/quasar.esm.prod.js +3 -3
  112. package/dist/quasar.sass +1 -1
  113. package/dist/quasar.umd.js +8 -7
  114. package/dist/quasar.umd.prod.js +3 -3
  115. package/dist/web-types/web-types.json +1 -1
  116. package/package.json +3 -3
  117. package/src/components/toolbar/QToolbar.test.js +45 -0
  118. package/src/components/toolbar/QToolbarTitle.test.js +45 -0
  119. package/src/composables/use-interval/use-interval.test.js +173 -0
  120. package/src/composables/use-render-cache/use-render-cache.js +2 -2
  121. package/src/composables/use-render-cache/use-render-cache.test.js +73 -0
  122. package/src/composables/use-split-attrs/use-split-attrs.test.js +55 -0
  123. package/src/composables/use-tick/use-tick.test.js +118 -0
  124. package/src/composables/use-timeout/use-timeout.test.js +169 -0
  125. package/src/plugins/addressbar/AddressbarColor.test.js +53 -0
  126. package/src/plugins/app-fullscreen/AppFullscreen.json +3 -1
  127. package/src/plugins/app-fullscreen/AppFullscreen.test.js +206 -0
  128. package/src/plugins/app-fullscreen/test/mock-fullscreen.js +43 -0
  129. package/src/plugins/app-visibility/AppVisibility.test.js +45 -0
  130. package/src/plugins/dark/Dark.test.js +158 -0
  131. package/src/plugins/platform/Platform.js +2 -1
  132. package/src/plugins/private.body/Body.test.js +28 -0
  133. package/src/plugins/private.history/History.test.js +38 -0
  134. package/src/plugins/screen/Screen.test.js +453 -0
  135. package/src/utils/css-var/get-css-var.test.js +12 -0
  136. package/src/utils/css-var/set-css-var.test.js +18 -0
  137. package/src/utils/dom/dom.test.js +222 -0
  138. package/src/utils/private.render/render.test.js +187 -0
  139. package/src/utils/private.selection/selection.test.js +40 -0
  140. package/src/utils/private.vm/vm.test.js +175 -0
@@ -0,0 +1,169 @@
1
+ import { describe, test, expect, vi, beforeEach, afterEach } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import { defineComponent } from 'vue'
4
+
5
+ import useTimeout from './use-timeout.js'
6
+
7
+ let wrapper
8
+
9
+ beforeEach(() => {
10
+ vi.useFakeTimers()
11
+ })
12
+
13
+ afterEach(() => {
14
+ vi.clearAllTimers()
15
+ vi.restoreAllMocks()
16
+
17
+ if (wrapper !== null) {
18
+ wrapper.unmount()
19
+ wrapper = null
20
+ }
21
+ })
22
+
23
+ describe('[useTimeout API]', () => {
24
+ describe('[Functions]', () => {
25
+ describe('[(function)default]', () => {
26
+ test('correctly registers a timeout (int)', () => {
27
+ const fn = vi.fn()
28
+
29
+ wrapper = mount(
30
+ defineComponent({
31
+ template: '<div />',
32
+ setup () {
33
+ const { registerTimeout } = useTimeout()
34
+
35
+ registerTimeout(fn, 100)
36
+ return {}
37
+ }
38
+ })
39
+ )
40
+
41
+ expect(fn).not.toHaveBeenCalled()
42
+ vi.advanceTimersByTime(99)
43
+ expect(fn).not.toHaveBeenCalled()
44
+ vi.advanceTimersByTime(1)
45
+ expect(fn).toHaveBeenCalledTimes(1)
46
+
47
+ vi.runAllTimers()
48
+ expect(fn).toHaveBeenCalledTimes(1)
49
+ })
50
+
51
+ test('correctly registers a timeout (str)', () => {
52
+ const fn = vi.fn()
53
+
54
+ wrapper = mount(
55
+ defineComponent({
56
+ template: '<div />',
57
+ setup () {
58
+ const { registerTimeout } = useTimeout()
59
+
60
+ registerTimeout(fn, '100')
61
+ return {}
62
+ }
63
+ })
64
+ )
65
+
66
+ expect(fn).not.toHaveBeenCalled()
67
+ vi.advanceTimersByTime(99)
68
+ expect(fn).not.toHaveBeenCalled()
69
+ vi.advanceTimersByTime(1)
70
+ expect(fn).toHaveBeenCalledTimes(1)
71
+
72
+ vi.runAllTimers()
73
+ expect(fn).toHaveBeenCalledTimes(1)
74
+ })
75
+
76
+ test('removeTimeout works correctly', () => {
77
+ const fn = vi.fn()
78
+
79
+ wrapper = mount(
80
+ defineComponent({
81
+ template: '<div />',
82
+ setup () {
83
+ const {
84
+ registerTimeout,
85
+ removeTimeout
86
+ } = useTimeout()
87
+
88
+ registerTimeout(fn, 100)
89
+ return { registerTimeout, removeTimeout }
90
+ }
91
+ })
92
+ )
93
+
94
+ expect(fn).not.toHaveBeenCalled()
95
+ vi.advanceTimersByTime(200)
96
+ expect(fn).toHaveBeenCalledTimes(1)
97
+
98
+ wrapper.vm.removeTimeout()
99
+ vi.runAllTimers()
100
+
101
+ expect(fn).toHaveBeenCalledTimes(1)
102
+
103
+ wrapper.vm.registerTimeout(fn, 100)
104
+ wrapper.unmount()
105
+
106
+ vi.runAllTimers()
107
+
108
+ expect(fn).toHaveBeenCalledTimes(1)
109
+ })
110
+
111
+ test('unmount stops timeout', () => {
112
+ const fn = vi.fn()
113
+
114
+ wrapper = mount(
115
+ defineComponent({
116
+ template: '<div />',
117
+ setup () {
118
+ const { registerTimeout } = useTimeout()
119
+
120
+ registerTimeout(fn, 100)
121
+ return {}
122
+ }
123
+ })
124
+ )
125
+
126
+ expect(fn).not.toHaveBeenCalled()
127
+
128
+ wrapper.unmount()
129
+ wrapper = null
130
+
131
+ vi.runAllTimers()
132
+
133
+ expect(fn).not.toHaveBeenCalled()
134
+ })
135
+
136
+ test('can override timeout', () => {
137
+ const fn1 = vi.fn()
138
+ const fn2 = vi.fn()
139
+
140
+ wrapper = mount(
141
+ defineComponent({
142
+ template: '<div />',
143
+ setup () {
144
+ const { registerTimeout } = useTimeout()
145
+ return { registerTimeout }
146
+ }
147
+ })
148
+ )
149
+
150
+ wrapper.vm.registerTimeout(fn1, 100)
151
+ vi.advanceTimersByTime(99)
152
+ expect(fn1).not.toHaveBeenCalled()
153
+
154
+ wrapper.vm.registerTimeout(fn2, 200)
155
+ vi.advanceTimersByTime(199)
156
+ expect(fn1).not.toHaveBeenCalled()
157
+ expect(fn2).not.toHaveBeenCalled()
158
+
159
+ vi.advanceTimersByTime(1)
160
+ expect(fn1).not.toHaveBeenCalled()
161
+ expect(fn2).toHaveBeenCalledTimes(1)
162
+
163
+ vi.runAllTimers()
164
+ expect(fn1).not.toHaveBeenCalled()
165
+ expect(fn2).toHaveBeenCalledTimes(1)
166
+ })
167
+ })
168
+ })
169
+ })
@@ -0,0 +1,53 @@
1
+ import { describe, test, expect, vi } from 'vitest'
2
+ import { mount, config } from '@vue/test-utils'
3
+
4
+ import AddressbarColor from './AddressbarColor.js'
5
+
6
+ // We override Quasar install so it installs this plugin
7
+ const quasarVuePlugin = config.global.plugins.find(entry => entry.name === 'Quasar')
8
+ const { install } = quasarVuePlugin
9
+
10
+ function mountPlugin (addressbarColor) {
11
+ quasarVuePlugin.install = app => install(app, {
12
+ config: { addressbarColor },
13
+ plugins: { AddressbarColor }
14
+ })
15
+
16
+ return mount({ template: '<div />' })
17
+ }
18
+
19
+ describe('[AddressbarColor API]', () => {
20
+ describe('[Injection]', () => {
21
+ test('is injected into $q', () => {
22
+ const wrapper = mountPlugin()
23
+ expect(AddressbarColor).toBe(wrapper.vm.$q.addressbarColor)
24
+ })
25
+ })
26
+
27
+ describe('[Methods]', () => {
28
+ describe('[(method)set]', () => {
29
+ test('should be callable', () => {
30
+ mountPlugin()
31
+
32
+ expect(
33
+ AddressbarColor.set('#ff0000')
34
+ ).toBeUndefined()
35
+ })
36
+
37
+ test('should be called automatically when $q.config.addressbarColor is set', () => {
38
+ const original = AddressbarColor.set
39
+
40
+ // override original since the real test would be on a
41
+ // mobile platform (and we can't test that here, yet)
42
+ AddressbarColor.set = vi.fn()
43
+ mountPlugin('#aabbcc')
44
+
45
+ expect.soft(AddressbarColor.set).toHaveBeenCalledTimes(1)
46
+ expect.soft(AddressbarColor.set).toHaveBeenCalledWith('#aabbcc')
47
+
48
+ // restore original
49
+ AddressbarColor.set = original
50
+ })
51
+ })
52
+ })
53
+ })
@@ -13,12 +13,14 @@
13
13
 
14
14
  "isActive": {
15
15
  "type": "Boolean",
16
- "desc": "Is Fullscreen active?"
16
+ "desc": "Is Fullscreen active?",
17
+ "reactive": true
17
18
  },
18
19
 
19
20
  "activeEl": {
20
21
  "type": [ "Element", "null" ],
21
22
  "desc": "The DOM element used as root for fullscreen, otherwise 'null'",
23
+ "reactive": true,
22
24
  "examples": [ "document.fullscreenElement", "null" ]
23
25
  }
24
26
  },
@@ -0,0 +1,206 @@
1
+ import { describe, test, expect, afterEach } from 'vitest'
2
+ import { mount, config } from '@vue/test-utils'
3
+
4
+ // jsdom hack
5
+ // this import should always sit before the AppFullscreen one
6
+ import { createMockedEl } from './test/mock-fullscreen.js'
7
+
8
+ import AppFullscreen from './AppFullscreen.js'
9
+
10
+ const mountPlugin = () => mount({ template: '<div />' })
11
+
12
+ // We override Quasar install so it installs this plugin
13
+ const quasarVuePlugin = config.global.plugins.find(entry => entry.name === 'Quasar')
14
+ const { install } = quasarVuePlugin
15
+ quasarVuePlugin.install = app => install(app, { plugins: { AppFullscreen } })
16
+
17
+ afterEach(async () => {
18
+ // ensure we don't leave test in fullscreen state
19
+ await AppFullscreen.exit()
20
+ })
21
+
22
+ describe('[AppFullscreen API]', () => {
23
+ describe('[Injection]', () => {
24
+ test('is injected into $q', () => {
25
+ const wrapper = mountPlugin()
26
+ expect(AppFullscreen).toMatchObject(wrapper.vm.$q.fullscreen)
27
+ })
28
+ })
29
+
30
+ describe('[Props]', () => {
31
+ describe('[(prop)isCapable]', () => {
32
+ test('is correct type', () => {
33
+ mountPlugin()
34
+ expect(AppFullscreen.isCapable).toBeTypeOf('boolean')
35
+ })
36
+ })
37
+
38
+ describe('[(prop)isActive]', () => {
39
+ test('is correct type', () => {
40
+ mountPlugin()
41
+ expect(AppFullscreen.isActive).toBeTypeOf('boolean')
42
+ })
43
+
44
+ test('is reactive', async () => {
45
+ mountPlugin()
46
+
47
+ expect(AppFullscreen.isActive).toBe(false)
48
+ await AppFullscreen.request()
49
+ expect(AppFullscreen.isActive).toBe(true)
50
+
51
+ await AppFullscreen.exit()
52
+ expect(AppFullscreen.isActive).toBe(false)
53
+ })
54
+ })
55
+
56
+ describe('[(prop)activeEl]', () => {
57
+ test('is correct type', () => {
58
+ mountPlugin()
59
+ expect(AppFullscreen.activeEl).$any([
60
+ expect.any(Element),
61
+ null
62
+ ])
63
+ })
64
+
65
+ test('is reactive', async () => {
66
+ mountPlugin()
67
+
68
+ expect(AppFullscreen.activeEl).toBe(null)
69
+ await AppFullscreen.request()
70
+ expect(AppFullscreen.activeEl).not.toBe(null)
71
+
72
+ await AppFullscreen.exit()
73
+ expect(AppFullscreen.activeEl).toBe(null)
74
+ })
75
+ })
76
+ })
77
+
78
+ describe('[Methods]', () => {
79
+ describe('[(method)request]', () => {
80
+ test('request()', async () => {
81
+ mountPlugin()
82
+
83
+ const result = AppFullscreen.request()
84
+ expect(
85
+ result
86
+ ).toBeInstanceOf(Promise)
87
+
88
+ await result
89
+ expect(AppFullscreen.isActive).toBe(true)
90
+ expect(AppFullscreen.activeEl).not.toBe(null)
91
+
92
+ await AppFullscreen.exit()
93
+ expect(AppFullscreen.isActive).toBe(false)
94
+ expect(AppFullscreen.activeEl).toBe(null)
95
+ })
96
+
97
+ test('request(el)', async () => {
98
+ mountPlugin()
99
+
100
+ const el = createMockedEl()
101
+
102
+ const result = AppFullscreen.request(el)
103
+ expect(
104
+ result
105
+ ).toBeInstanceOf(Promise)
106
+
107
+ await result
108
+
109
+ expect(el.requestFullscreen).toHaveBeenCalledTimes(1)
110
+ expect(AppFullscreen.isActive).toBe(true)
111
+ expect(AppFullscreen.activeEl).toBe(el)
112
+
113
+ await AppFullscreen.exit()
114
+ expect(AppFullscreen.isActive).toBe(false)
115
+ expect(AppFullscreen.activeEl).toBe(null)
116
+ })
117
+
118
+ test('call request(el) 2 times', async () => {
119
+ mountPlugin()
120
+
121
+ const el = createMockedEl()
122
+
123
+ const result = AppFullscreen.request(el)
124
+ expect(
125
+ result
126
+ ).toBeInstanceOf(Promise)
127
+
128
+ await result
129
+ expect(el.requestFullscreen).toHaveBeenCalledTimes(1)
130
+ expect(AppFullscreen.activeEl).toBe(el)
131
+
132
+ await AppFullscreen.request(el)
133
+ expect(el.requestFullscreen).toHaveBeenCalledTimes(1)
134
+ expect(AppFullscreen.activeEl).toBe(el)
135
+
136
+ await AppFullscreen.exit()
137
+ expect(AppFullscreen.activeEl).toBe(null)
138
+ })
139
+ })
140
+
141
+ describe('[(method)exit]', () => {
142
+ test('should be callable', () => {
143
+ mountPlugin()
144
+ expect(
145
+ AppFullscreen.exit()
146
+ ).toBeInstanceOf(Promise)
147
+ })
148
+
149
+ test('request() + exit()', async () => {
150
+ mountPlugin()
151
+
152
+ const result = AppFullscreen.request()
153
+ expect(
154
+ result
155
+ ).toBeInstanceOf(Promise)
156
+
157
+ await result
158
+ expect(AppFullscreen.isActive).toBe(true)
159
+ expect(AppFullscreen.activeEl).not.toBe(null)
160
+
161
+ await AppFullscreen.exit()
162
+ expect(AppFullscreen.isActive).toBe(false)
163
+ expect(AppFullscreen.activeEl).toBe(null)
164
+ })
165
+ })
166
+
167
+ describe('[(method)toggle]', () => {
168
+ test('toggle()', async () => {
169
+ mountPlugin()
170
+
171
+ const result = AppFullscreen.toggle()
172
+ expect(
173
+ result
174
+ ).toBeInstanceOf(Promise)
175
+
176
+ await result
177
+ expect(AppFullscreen.isActive).toBe(true)
178
+ expect(AppFullscreen.activeEl).not.toBe(null)
179
+
180
+ await AppFullscreen.toggle()
181
+ expect(AppFullscreen.isActive).toBe(false)
182
+ expect(AppFullscreen.activeEl).toBe(null)
183
+ })
184
+
185
+ test('toggle(el)', async () => {
186
+ mountPlugin()
187
+
188
+ const el = createMockedEl()
189
+
190
+ const result = AppFullscreen.toggle(el)
191
+ expect(
192
+ result
193
+ ).toBeInstanceOf(Promise)
194
+
195
+ await result
196
+ expect(el.requestFullscreen).toHaveBeenCalledTimes(1)
197
+ expect(AppFullscreen.isActive).toBe(true)
198
+ expect(AppFullscreen.activeEl).toBe(el)
199
+
200
+ await AppFullscreen.toggle()
201
+ expect(AppFullscreen.isActive).toBe(false)
202
+ expect(AppFullscreen.activeEl).toBe(null)
203
+ })
204
+ })
205
+ })
206
+ })
@@ -0,0 +1,43 @@
1
+ import { vi } from 'vitest'
2
+
3
+ /**
4
+ * jsdom does not support Fullscreen API,
5
+ * so we mock the functionality
6
+ */
7
+
8
+ export function mockedRequestFullscreen (el = document.documentElement) {
9
+ document.fullscreenElement = el
10
+ mockedToggleFullscreen()
11
+ }
12
+
13
+ export function mockedExitFullscreen () {
14
+ document.fullscreenElement = null
15
+ mockedToggleFullscreen()
16
+ }
17
+
18
+ export function mockedToggleFullscreen () {
19
+ document.onfullscreenchange()
20
+ }
21
+
22
+ export function createMockedEl () {
23
+ const el = document.createElement('div')
24
+ el.setAttribute('tabindex', '0')
25
+ document.body.appendChild(el)
26
+
27
+ el.requestFullscreen = vi.fn(() => {
28
+ document.fullscreenElement = el
29
+ mockedToggleFullscreen()
30
+ })
31
+
32
+ el.exitFullscreen = mockedExitFullscreen
33
+
34
+ onTestFinished(() => { el.remove() })
35
+
36
+ return el
37
+ }
38
+
39
+ document.documentElement.requestFullscreen = mockedRequestFullscreen
40
+ document.documentElement.exitFullscreen = mockedExitFullscreen
41
+
42
+ document.requestFullscreen = mockedRequestFullscreen
43
+ document.exitFullscreen = mockedExitFullscreen
@@ -0,0 +1,45 @@
1
+ import { describe, test, expect } from 'vitest'
2
+ import { mount, config } from '@vue/test-utils'
3
+
4
+ import AppVisibility from './AppVisibility.js'
5
+
6
+ const mountPlugin = () => mount({ template: '<div />' })
7
+
8
+ // We override Quasar install so it installs this plugin
9
+ const quasarVuePlugin = config.global.plugins.find(entry => entry.name === 'Quasar')
10
+ const { install } = quasarVuePlugin
11
+ quasarVuePlugin.install = app => install(app, { plugins: { AppVisibility } })
12
+
13
+ describe('[AppVisibility API]', () => {
14
+ describe('[Injection]', () => {
15
+ test('is injected into $q', () => {
16
+ const wrapper = mountPlugin()
17
+ expect(wrapper.vm.$q.appVisible).toBe(AppVisibility.appVisible)
18
+ })
19
+ })
20
+
21
+ describe('[Props]', () => {
22
+ describe('[(prop)appVisible]', () => {
23
+ test('is correct type', () => {
24
+ mountPlugin()
25
+ expect(AppVisibility.appVisible).toBe(true)
26
+ })
27
+
28
+ test('is reactive', () => {
29
+ const wrapper = mountPlugin()
30
+ expect(AppVisibility.appVisible).toBe(true)
31
+
32
+ // jsdom hack
33
+ Object.defineProperty(document, 'hidden', {
34
+ configurable: true,
35
+ get: () => true
36
+ })
37
+
38
+ document.dispatchEvent(new Event('visibilitychange'))
39
+
40
+ expect(AppVisibility.appVisible).toBe(false)
41
+ expect(wrapper.vm.$q.appVisible).toBe(false)
42
+ })
43
+ })
44
+ })
45
+ })
@@ -0,0 +1,158 @@
1
+ import { describe, test, expect, vi } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+
4
+ import Dark from './Dark.js'
5
+
6
+ const mountPlugin = () => mount({ template: '<div />' })
7
+
8
+ describe('[Dark API]', () => {
9
+ describe('[Injection]', () => {
10
+ test('is injected into $q', () => {
11
+ const wrapper = mountPlugin()
12
+ expect(Dark).toMatchObject(wrapper.vm.$q.dark)
13
+ })
14
+ })
15
+
16
+ describe('[Props]', () => {
17
+ describe('[(prop)isActive]', () => {
18
+ test('is correct type', () => {
19
+ mountPlugin()
20
+ expect(Dark.isActive).toBeTypeOf('boolean')
21
+ })
22
+
23
+ test('is reactive', () => {
24
+ const { vm: { $q } } = mountPlugin()
25
+
26
+ expect(Dark.isActive).toBe(false)
27
+ expect($q.dark.isActive).toBe(false)
28
+ expect(
29
+ document.body.classList.contains('body--dark')
30
+ ).toBe(false)
31
+
32
+ Dark.set(true)
33
+
34
+ expect(Dark.isActive).toBe(true)
35
+ expect($q.dark.isActive).toBe(true)
36
+ expect(
37
+ document.body.classList.contains('body--dark')
38
+ ).toBe(true)
39
+ })
40
+ })
41
+
42
+ describe('[(prop)mode]', () => {
43
+ test('is correct type', () => {
44
+ mountPlugin()
45
+ expect([ 'auto', true, false ]).toContain(Dark.mode)
46
+ })
47
+ })
48
+ })
49
+
50
+ describe('[Methods]', () => {
51
+ describe('[(method)set]', () => {
52
+ test('should be callable', () => {
53
+ const { vm: { $q } } = mountPlugin()
54
+
55
+ expect(
56
+ Dark.set(true)
57
+ ).toBeUndefined()
58
+
59
+ expect(Dark.isActive).toBe(true)
60
+ expect($q.dark.isActive).toBe(true)
61
+ expect(
62
+ document.body.classList.contains('body--dark')
63
+ ).toBe(true)
64
+
65
+ expect(
66
+ Dark.set(false)
67
+ ).toBeUndefined()
68
+
69
+ expect(Dark.isActive).toBe(false)
70
+ expect($q.dark.isActive).toBe(false)
71
+ expect(
72
+ document.body.classList.contains('body--dark')
73
+ ).toBe(false)
74
+ })
75
+
76
+ test('should handle auto mode', () => {
77
+ const { vm: { $q } } = mountPlugin()
78
+
79
+ // jsdom hack
80
+ const media = {
81
+ matches: true,
82
+ addListener: vi.fn(),
83
+ removeListener: vi.fn()
84
+ }
85
+ window.matchMedia = vi.fn(() => media)
86
+
87
+ Dark.set('auto')
88
+
89
+ expect(Dark.mode).toBe('auto')
90
+
91
+ expect(media.addListener).toHaveBeenCalledTimes(1)
92
+ expect(media.removeListener).not.toHaveBeenCalled()
93
+
94
+ expect(Dark.isActive).toBe(true)
95
+ expect($q.dark.isActive).toBe(true)
96
+ expect(
97
+ document.body.classList.contains('body--dark')
98
+ ).toBe(true)
99
+
100
+ media.matches = false
101
+ Dark.__updateMedia()
102
+
103
+ expect(media.addListener).toHaveBeenCalledTimes(1)
104
+ expect(media.removeListener).not.toHaveBeenCalled()
105
+
106
+ expect(Dark.isActive).toBe(false)
107
+ expect($q.dark.isActive).toBe(false)
108
+ expect(
109
+ document.body.classList.contains('body--dark')
110
+ ).toBe(false)
111
+
112
+ Dark.set(true)
113
+ expect(Dark.mode).not.toBe('auto')
114
+
115
+ expect(media.addListener).toHaveBeenCalledTimes(1)
116
+ expect(media.removeListener).toHaveBeenCalledTimes(1)
117
+
118
+ expect(Dark.isActive).toBe(true)
119
+ expect($q.dark.isActive).toBe(true)
120
+ expect(
121
+ document.body.classList.contains('body--dark')
122
+ ).toBe(true)
123
+ })
124
+ })
125
+
126
+ describe('[(method)toggle]', () => {
127
+ test('should be callable', () => {
128
+ const { vm: { $q } } = mountPlugin()
129
+
130
+ Dark.set(true)
131
+
132
+ expect(Dark.isActive).toBe(true)
133
+ expect($q.dark.isActive).toBe(true)
134
+ expect(
135
+ document.body.classList.contains('body--dark')
136
+ ).toBe(true)
137
+
138
+ expect(
139
+ Dark.toggle()
140
+ ).toBeUndefined()
141
+
142
+ expect(Dark.isActive).toBe(false)
143
+ expect($q.dark.isActive).toBe(false)
144
+ expect(
145
+ document.body.classList.contains('body--dark')
146
+ ).toBe(false)
147
+
148
+ Dark.toggle()
149
+
150
+ expect(Dark.isActive).toBe(true)
151
+ expect($q.dark.isActive).toBe(true)
152
+ expect(
153
+ document.body.classList.contains('body--dark')
154
+ ).toBe(true)
155
+ })
156
+ })
157
+ })
158
+ })
@@ -124,8 +124,9 @@ function getPlatform (UA) {
124
124
  browser.firefox = true
125
125
  matched.browser = 'firefox'
126
126
  }
127
+
127
128
  // Set iOS if on iPod, iPad or iPhone
128
- else if (browser.ipod || browser.ipad || browser.iphone) {
129
+ if (browser.ipod || browser.ipad || browser.iphone) {
129
130
  browser.ios = true
130
131
  }
131
132