@api-client/ui 0.0.11 → 0.0.13

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 (242) hide show
  1. package/.eslintrc +8 -0
  2. package/demo/elements/authorization/cc.ts +56 -27
  3. package/demo/elements/index.html +3 -0
  4. package/demo/elements/store/file-picker.html +15 -0
  5. package/demo/elements/store/file-picker.ts +134 -0
  6. package/demo/elements/store/index.html +19 -0
  7. package/demo/store/StorePlugin.js +1 -0
  8. package/dist/bindings/base/FileBindings.d.ts +4 -0
  9. package/dist/bindings/base/FileBindings.d.ts.map +1 -1
  10. package/dist/bindings/base/FileBindings.js +21 -1
  11. package/dist/bindings/base/FileBindings.js.map +1 -1
  12. package/dist/bindings/base/StoreBindings.d.ts +6 -17
  13. package/dist/bindings/base/StoreBindings.d.ts.map +1 -1
  14. package/dist/bindings/base/StoreBindings.js +15 -60
  15. package/dist/bindings/base/StoreBindings.js.map +1 -1
  16. package/dist/bindings/web/WebFileBindings.js +1 -1
  17. package/dist/bindings/web/WebFileBindings.js.map +1 -1
  18. package/dist/define/http/certificate-add.d.ts +9 -0
  19. package/dist/define/http/certificate-add.d.ts.map +1 -0
  20. package/dist/define/http/certificate-add.js +10 -0
  21. package/dist/define/http/certificate-add.js.map +1 -0
  22. package/dist/define/store/file-picker.d.ts +9 -0
  23. package/dist/define/store/file-picker.d.ts.map +1 -0
  24. package/dist/define/store/file-picker.js +10 -0
  25. package/dist/define/store/file-picker.js.map +1 -0
  26. package/dist/define/{files → store}/share-file.d.ts +1 -1
  27. package/dist/define/store/share-file.d.ts.map +1 -0
  28. package/dist/define/{files → store}/share-file.js +2 -2
  29. package/dist/define/store/share-file.js.map +1 -0
  30. package/dist/define/ui/ui-segmented-button-set.d.ts +1 -1
  31. package/dist/define/ui/ui-segmented-button-set.d.ts.map +1 -1
  32. package/dist/define/ui/ui-segmented-button-set.js.map +1 -1
  33. package/dist/elements/authorization/ui/CC.styles.d.ts.map +1 -1
  34. package/dist/elements/authorization/ui/CC.styles.js +4 -9
  35. package/dist/elements/authorization/ui/CC.styles.js.map +1 -1
  36. package/dist/elements/authorization/ui/CcAuthorization.d.ts +14 -29
  37. package/dist/elements/authorization/ui/CcAuthorization.d.ts.map +1 -1
  38. package/dist/elements/authorization/ui/CcAuthorization.js +67 -158
  39. package/dist/elements/authorization/ui/CcAuthorization.js.map +1 -1
  40. package/dist/elements/http/CertificateAdd.element.d.ts +91 -0
  41. package/dist/elements/http/CertificateAdd.element.d.ts.map +1 -0
  42. package/dist/elements/http/CertificateAdd.element.js +389 -0
  43. package/dist/elements/http/CertificateAdd.element.js.map +1 -0
  44. package/dist/elements/http/CertificateAdd.styles.d.ts +3 -0
  45. package/dist/elements/http/CertificateAdd.styles.d.ts.map +1 -0
  46. package/dist/elements/http/CertificateAdd.styles.js +61 -0
  47. package/dist/elements/http/CertificateAdd.styles.js.map +1 -0
  48. package/dist/elements/project/ProjectRunReport.d.ts +2 -1
  49. package/dist/elements/project/ProjectRunReport.d.ts.map +1 -1
  50. package/dist/elements/project/ProjectRunReport.js.map +1 -1
  51. package/dist/elements/store/FilePicker.element.d.ts +87 -0
  52. package/dist/elements/store/FilePicker.element.d.ts.map +1 -0
  53. package/dist/elements/store/FilePicker.element.js +263 -0
  54. package/dist/elements/store/FilePicker.element.js.map +1 -0
  55. package/dist/elements/store/FilePicker.styles.d.ts +3 -0
  56. package/dist/elements/store/FilePicker.styles.d.ts.map +1 -0
  57. package/dist/elements/store/FilePicker.styles.js +73 -0
  58. package/dist/elements/store/FilePicker.styles.js.map +1 -0
  59. package/dist/elements/store/FilesLib.d.ts +10 -0
  60. package/dist/elements/store/FilesLib.d.ts.map +1 -0
  61. package/dist/elements/store/FilesLib.js +38 -0
  62. package/dist/elements/store/FilesLib.js.map +1 -0
  63. package/dist/elements/{files/ShareFile.d.ts → store/ShareFile.element.d.ts} +1 -1
  64. package/dist/elements/store/ShareFile.element.d.ts.map +1 -0
  65. package/dist/elements/{files/ShareFile.js → store/ShareFile.element.js} +1 -1
  66. package/dist/elements/store/ShareFile.element.js.map +1 -0
  67. package/dist/elements/store/ShareFile.styles.d.ts.map +1 -0
  68. package/dist/elements/{files → store}/ShareFile.styles.js.map +1 -1
  69. package/dist/events/EventTypes.d.ts +7 -7
  70. package/dist/events/EventTypes.d.ts.map +1 -1
  71. package/dist/events/EventTypes.js +8 -7
  72. package/dist/events/EventTypes.js.map +1 -1
  73. package/dist/events/Events.d.ts +7 -1
  74. package/dist/events/Events.d.ts.map +1 -1
  75. package/dist/events/Events.js +2 -0
  76. package/dist/events/Events.js.map +1 -1
  77. package/dist/events/FilesystemEvents.d.ts +8 -0
  78. package/dist/events/FilesystemEvents.d.ts.map +1 -0
  79. package/dist/events/FilesystemEvents.js +59 -0
  80. package/dist/events/FilesystemEvents.js.map +1 -0
  81. package/dist/events/HttpClientEvents.d.ts +0 -2
  82. package/dist/events/HttpClientEvents.d.ts.map +1 -1
  83. package/dist/events/HttpClientEvents.js +0 -2
  84. package/dist/events/HttpClientEvents.js.map +1 -1
  85. package/dist/events/StoreEvents.d.ts +8 -1
  86. package/dist/events/StoreEvents.d.ts.map +1 -1
  87. package/dist/events/StoreEvents.js +19 -0
  88. package/dist/events/StoreEvents.js.map +1 -1
  89. package/dist/http-client/idb/Arc18DataUpgrade.d.ts +0 -8
  90. package/dist/http-client/idb/Arc18DataUpgrade.d.ts.map +1 -1
  91. package/dist/http-client/idb/Arc18DataUpgrade.js +11 -206
  92. package/dist/http-client/idb/Arc18DataUpgrade.js.map +1 -1
  93. package/dist/http-client/store/StoreBroadcast.d.ts +0 -5
  94. package/dist/http-client/store/StoreBroadcast.d.ts.map +1 -1
  95. package/dist/http-client/store/StoreBroadcast.js +0 -7
  96. package/dist/http-client/store/StoreBroadcast.js.map +1 -1
  97. package/dist/lib/files/FileUtils.d.ts +9 -0
  98. package/dist/lib/files/FileUtils.d.ts.map +1 -0
  99. package/dist/lib/files/FileUtils.js +13 -0
  100. package/dist/lib/files/FileUtils.js.map +1 -0
  101. package/dist/mixins/RouteMixin.d.ts +4 -0
  102. package/dist/mixins/RouteMixin.d.ts.map +1 -1
  103. package/dist/mixins/RouteMixin.js +1 -0
  104. package/dist/mixins/RouteMixin.js.map +1 -1
  105. package/dist/pages/ApplicationScreen.d.ts +1 -1
  106. package/dist/pages/ApplicationScreen.d.ts.map +1 -1
  107. package/dist/pages/ApplicationScreen.js +4 -2
  108. package/dist/pages/ApplicationScreen.js.map +1 -1
  109. package/dist/pages/api-client/ApiClient.screen.d.ts +4 -6
  110. package/dist/pages/api-client/ApiClient.screen.d.ts.map +1 -1
  111. package/dist/pages/api-client/ApiClient.screen.js +39 -31
  112. package/dist/pages/api-client/ApiClient.screen.js.map +1 -1
  113. package/dist/pages/api-client/ApiClient.styles.d.ts.map +1 -1
  114. package/dist/pages/api-client/ApiClient.styles.js +0 -12
  115. package/dist/pages/api-client/ApiClient.styles.js.map +1 -1
  116. package/dist/pages/api-client/Authenticate.screen.d.ts +1 -1
  117. package/dist/pages/api-client/Authenticate.screen.d.ts.map +1 -1
  118. package/dist/pages/api-client/Authenticate.screen.js +2 -2
  119. package/dist/pages/api-client/Authenticate.screen.js.map +1 -1
  120. package/dist/pages/api-client/pages/Files.page.d.ts +6 -35
  121. package/dist/pages/api-client/pages/Files.page.d.ts.map +1 -1
  122. package/dist/pages/api-client/pages/Files.page.js +45 -141
  123. package/dist/pages/api-client/pages/Files.page.js.map +1 -1
  124. package/dist/pages/api-client/pages/Shared.page.d.ts +1 -5
  125. package/dist/pages/api-client/pages/Shared.page.d.ts.map +1 -1
  126. package/dist/pages/api-client/pages/Shared.page.js +1 -40
  127. package/dist/pages/api-client/pages/Shared.page.js.map +1 -1
  128. package/dist/pages/demo/DemoPage.d.ts +7 -0
  129. package/dist/pages/demo/DemoPage.d.ts.map +1 -1
  130. package/dist/pages/demo/DemoPage.js +14 -0
  131. package/dist/pages/demo/DemoPage.js.map +1 -1
  132. package/dist/store/FileSystem.d.ts +90 -0
  133. package/dist/store/FileSystem.d.ts.map +1 -0
  134. package/dist/store/FileSystem.js +260 -0
  135. package/dist/store/FileSystem.js.map +1 -0
  136. package/dist/styles/global-styles.d.ts.map +1 -1
  137. package/dist/styles/global-styles.js +7 -0
  138. package/dist/styles/global-styles.js.map +1 -1
  139. package/dist/ui/button/SegmentedButtonsSet.d.ts +14 -0
  140. package/dist/ui/button/SegmentedButtonsSet.d.ts.map +1 -1
  141. package/dist/ui/button/SegmentedButtonsSet.js.map +1 -1
  142. package/dist/ui/icons/Icons.d.ts +2 -1
  143. package/dist/ui/icons/Icons.d.ts.map +1 -1
  144. package/dist/ui/icons/Icons.js +1 -0
  145. package/dist/ui/icons/Icons.js.map +1 -1
  146. package/dist/ui/list/UiDropdownList.d.ts +9 -1
  147. package/dist/ui/list/UiDropdownList.d.ts.map +1 -1
  148. package/dist/ui/list/UiDropdownList.js +39 -17
  149. package/dist/ui/list/UiDropdownList.js.map +1 -1
  150. package/dist/ui/list/UiList.d.ts +6 -1
  151. package/dist/ui/list/UiList.d.ts.map +1 -1
  152. package/dist/ui/list/UiList.js +24 -9
  153. package/dist/ui/list/UiList.js.map +1 -1
  154. package/dist/ui/notification/SnackNotifications.d.ts +1 -0
  155. package/dist/ui/notification/SnackNotifications.d.ts.map +1 -1
  156. package/dist/ui/notification/SnackNotifications.js +7 -0
  157. package/dist/ui/notification/SnackNotifications.js.map +1 -1
  158. package/dist/ui/table/DataTable.d.ts +4 -0
  159. package/dist/ui/table/DataTable.d.ts.map +1 -1
  160. package/dist/ui/table/DataTable.js +23 -1
  161. package/dist/ui/table/DataTable.js.map +1 -1
  162. package/package.json +1 -1
  163. package/src/bindings/base/FileBindings.ts +25 -1
  164. package/src/bindings/base/StoreBindings.ts +16 -73
  165. package/src/bindings/web/WebFileBindings.ts +1 -1
  166. package/src/define/http/certificate-add.ts +12 -0
  167. package/src/define/store/file-picker.ts +12 -0
  168. package/src/define/{files → store}/share-file.ts +2 -2
  169. package/src/define/ui/ui-segmented-button-set.ts +1 -1
  170. package/src/elements/authorization/ui/CC.styles.ts +4 -9
  171. package/src/elements/authorization/ui/CcAuthorization.ts +67 -167
  172. package/src/elements/http/CertificateAdd.element.ts +443 -0
  173. package/src/elements/http/CertificateAdd.styles.ts +61 -0
  174. package/src/elements/project/ProjectRunReport.ts +2 -1
  175. package/src/elements/store/FilePicker.element.ts +297 -0
  176. package/src/elements/store/FilePicker.styles.ts +73 -0
  177. package/src/elements/store/FilesLib.ts +32 -0
  178. package/src/events/EventTypes.ts +8 -7
  179. package/src/events/Events.ts +2 -0
  180. package/src/events/FilesystemEvents.ts +63 -0
  181. package/src/events/HttpClientEvents.ts +0 -2
  182. package/src/events/StoreEvents.ts +21 -1
  183. package/src/http-client/idb/Arc18DataUpgrade.ts +84 -84
  184. package/src/http-client/store/StoreBroadcast.ts +0 -8
  185. package/src/lib/files/FileUtils.ts +12 -0
  186. package/src/mixins/RouteMixin.ts +8 -1
  187. package/src/pages/ApplicationScreen.ts +5 -3
  188. package/src/pages/api-client/ApiClient.screen.ts +42 -33
  189. package/src/pages/api-client/ApiClient.styles.ts +0 -12
  190. package/src/pages/api-client/Authenticate.screen.ts +2 -2
  191. package/src/pages/api-client/pages/Files.page.ts +48 -164
  192. package/src/pages/api-client/pages/Shared.page.ts +2 -40
  193. package/src/pages/demo/DemoPage.ts +17 -0
  194. package/src/store/FileSystem.ts +325 -0
  195. package/src/styles/global-styles.ts +7 -0
  196. package/src/ui/button/SegmentedButtonsSet.ts +16 -1
  197. package/src/ui/icons/Icons.ts +2 -1
  198. package/src/ui/list/UiDropdownList.ts +44 -17
  199. package/src/ui/list/UiList.ts +26 -10
  200. package/src/ui/notification/SnackNotifications.ts +8 -0
  201. package/src/ui/table/DataTable.ts +29 -3
  202. package/test/elements/http/BodyFormdataEditorElement.test.ts +458 -454
  203. package/test/elements/http/BodyMultipartEditorElement.test.ts +609 -605
  204. package/test/elements/http/BodyRawEditorElement.test.ts +60 -56
  205. package/test/elements/http/CertificateAdd.test.ts +430 -0
  206. package/test/elements/store/FilePicker.test.ts +241 -0
  207. package/test/env.js +3 -0
  208. package/test/events/EventTypes.test.ts +0 -22
  209. package/test/helpers/StoreHelper.ts +390 -0
  210. package/test/helpers/UiMock.ts +19 -2
  211. package/tsconfig.eslint.json +3 -1
  212. package/web-test-runner.config.mjs +50 -3
  213. package/dist/define/files/share-file.d.ts.map +0 -1
  214. package/dist/define/files/share-file.js.map +0 -1
  215. package/dist/elements/files/ShareFile.d.ts.map +0 -1
  216. package/dist/elements/files/ShareFile.js.map +0 -1
  217. package/dist/elements/files/ShareFile.styles.d.ts.map +0 -1
  218. package/dist/events/http-client/models/CertificatesEvents.d.ts +0 -12
  219. package/dist/events/http-client/models/CertificatesEvents.d.ts.map +0 -1
  220. package/dist/events/http-client/models/CertificatesEvents.js +0 -18
  221. package/dist/events/http-client/models/CertificatesEvents.js.map +0 -1
  222. package/dist/http-client/idb/AuthDataModel.d.ts +0 -60
  223. package/dist/http-client/idb/AuthDataModel.d.ts.map +0 -1
  224. package/dist/http-client/idb/AuthDataModel.js +0 -150
  225. package/dist/http-client/idb/AuthDataModel.js.map +0 -1
  226. package/dist/http-client/idb/HostsModel.d.ts +0 -25
  227. package/dist/http-client/idb/HostsModel.d.ts.map +0 -1
  228. package/dist/http-client/idb/HostsModel.js +0 -82
  229. package/dist/http-client/idb/HostsModel.js.map +0 -1
  230. package/dist/http-client/idb/LegacyMockedStore.d.ts +0 -214
  231. package/dist/http-client/idb/LegacyMockedStore.d.ts.map +0 -1
  232. package/dist/http-client/idb/LegacyMockedStore.js +0 -486
  233. package/dist/http-client/idb/LegacyMockedStore.js.map +0 -1
  234. package/src/events/http-client/models/CertificatesEvents.ts +0 -23
  235. package/src/http-client/idb/AuthDataModel.ts +0 -175
  236. package/src/http-client/idb/HostsModel.ts +0 -125
  237. package/src/http-client/idb/LegacyMockedStore.ts +0 -544
  238. package/test/apic-ui.test.ts +0 -31
  239. /package/dist/elements/{files → store}/ShareFile.styles.d.ts +0 -0
  240. /package/dist/elements/{files → store}/ShareFile.styles.js +0 -0
  241. /package/src/elements/{files/ShareFile.ts → store/ShareFile.element.ts} +0 -0
  242. /package/src/elements/{files → store}/ShareFile.styles.ts +0 -0
@@ -0,0 +1,241 @@
1
+ /* eslint-disable no-await-in-loop */
2
+ import { assert, fixture, html, oneEvent } from '@open-wc/testing';
3
+ import sinon from 'sinon';
4
+ import {
5
+ FolderKind,
6
+ IFolder,
7
+ ProjectKind
8
+ } from '@api-client/core/build/browser.js';
9
+ import type FilePicker from '../../../src/elements/store/FilePicker.element.js';
10
+ import type UiListItem from '../../../src/ui/list/UiListItem.js';
11
+ import env from '../../env.js';
12
+ import HttpHelper from '../../helpers/StoreHelper.js';
13
+ import type { FilePickerClosingReason } from '../../../src/elements/store/FilePicker.element.js';
14
+ import '../../../src/define/store/file-picker.js';
15
+
16
+ describe('elements', () => {
17
+ describe('store', () => {
18
+ describe('FilePicker', () => {
19
+ async function manualFixture(): Promise<FilePicker> {
20
+ return fixture(html`<file-picker .debounceTimeout="${0}" manualQuery></file-picker>`);
21
+ }
22
+
23
+ let helper: HttpHelper;
24
+ let baseUri: string;
25
+ before(async () => {
26
+ baseUri = env.store.single;
27
+ helper = new HttpHelper(baseUri);
28
+ await helper.initializeSdk();
29
+ await helper.addUser({ defaultUser: true });
30
+ await helper.addSpace({ defaultSpace: true });
31
+ });
32
+
33
+ after(async () => {
34
+ await helper.clearFiles();
35
+ await helper.clearSpaces();
36
+ await helper.clearPermissions();
37
+ await helper.clearSessions();
38
+ });
39
+
40
+ describe('file list (store has files)', () => {
41
+ let folders: IFolder[];
42
+ let emptyFolder: IFolder;
43
+
44
+ before(async () => {
45
+ folders = await helper.generateFolders(10);
46
+ await helper.generateProjects(20);
47
+ for (const parent of folders) {
48
+ await helper.addFolder({ parent: parent.key });
49
+ await helper.addProject({ parent: parent.key });
50
+ }
51
+ // empty folder without children
52
+ emptyFolder = await helper.addFolder();
53
+ });
54
+
55
+ after(async () => {
56
+ await helper.clearFiles();
57
+ await helper.clearPermissions();
58
+ });
59
+
60
+ it('renders the list of files', async () => {
61
+ const element = await manualFixture();
62
+ element.limit = 20;
63
+ await element.fs.queryPage();
64
+ await element.updateComplete;
65
+ assert.lengthOf(element.fs.files, 20, 'has the page of results');
66
+ assert.typeOf(element.fs.cursor, 'string', 'has the page cursor');
67
+ assert.isUndefined(element.fs.breadcrumbs, 'has no breadcrumbs for the root');
68
+ assert.isUndefined(element.selectedKey, 'has no selectedKey');
69
+ const list = element.shadowRoot!.querySelector('ui-list')!;
70
+ const items = Array.from(list.querySelectorAll('ui-list-item'));
71
+ assert.lengthOf(items, 20, 'renders the list of files');
72
+ });
73
+
74
+ it('enters an empty folder', async () => {
75
+ const element = await manualFixture();
76
+ await element.fs.queryPage();
77
+ await element.updateComplete;
78
+ const eventPromise = oneEvent(element, 'querycomplete');
79
+ const item = element.shadowRoot!.querySelector(`ui-list-item[data-key="${emptyFolder.key}"]`) as UiListItem;
80
+ item.click();
81
+ assert.equal(element.fs.parent, emptyFolder.key, 'selects a parent');
82
+ assert.deepEqual(element.fs.files, [], 'resets files');
83
+ assert.isUndefined(element.fs.cursor, 'resets page cursor');
84
+ await eventPromise;
85
+ assert.deepEqual(element.fs.files, [], 'has no files in the sub-folder');
86
+ const node = element.shadowRoot!.querySelector('.no-files')!;
87
+ assert.ok(node, 'has the no-files message');
88
+ assert.equal(node.textContent!.trim(), 'No files in this view.');
89
+ });
90
+
91
+ it('enters a folder with files', async () => {
92
+ const element = await manualFixture();
93
+ await element.fs.queryPage();
94
+ await element.updateComplete;
95
+ const cursorBefore = element.fs.cursor;
96
+ const [f1] = folders;
97
+ const item = element.shadowRoot!.querySelector(`ui-list-item[data-key="${f1.key}"]`) as UiListItem;
98
+ item.click();
99
+ await oneEvent(element, 'querycomplete');
100
+ await element.updateComplete;
101
+ assert.equal(element.fs.parent, f1.key, 'selects a parent');
102
+ assert.lengthOf(element.fs.files, 2, 'has both files');
103
+ assert.typeOf(element.fs.cursor, 'string', 'has the page cursor');
104
+ assert.notEqual(element.fs.cursor, cursorBefore, 'has different cursor');
105
+ const node = element.shadowRoot!.querySelector('.no-files');
106
+ assert.notOk(node, 'has no empty message');
107
+
108
+ const list = element.shadowRoot!.querySelector('ui-list')!;
109
+ const items = Array.from(list.querySelectorAll('ui-list-item'));
110
+ assert.lengthOf(items, 2, 'renders the list of files');
111
+ });
112
+
113
+ it('goes to a deep child', async () => {
114
+ const element = await manualFixture();
115
+ await element.fs.queryPage();
116
+ await element.updateComplete;
117
+ const [f1] = folders;
118
+ const item = element.shadowRoot!.querySelector(`ui-list-item[data-key="${f1.key}"]`) as UiListItem;
119
+ item.click();
120
+ await oneEvent(element, 'querycomplete');
121
+ await element.updateComplete;
122
+ const item2 = element.shadowRoot!.querySelector(`ui-list-item[data-kind="${FolderKind}"]`) as UiListItem;
123
+ item2.click();
124
+ await oneEvent(element, 'querycomplete');
125
+ await element.updateComplete;
126
+ assert.deepEqual(element.fs.files, [], 'has no files in the sub-folder');
127
+ const node = element.shadowRoot!.querySelector('.no-files')!;
128
+ assert.ok(node, 'has the no-files message');
129
+ assert.equal(node.textContent!.trim(), 'No files in this view.');
130
+ });
131
+
132
+ it('moves to the parent folder', async () => {
133
+ const [f1] = folders;
134
+ const files = await helper.sdk.file.list(helper.space!.key, [], { parent: f1.key });
135
+
136
+ const child = files.items.find(i => i.kind === FolderKind) as IFolder;
137
+ const element = await manualFixture();
138
+ element.limit = 20;
139
+ element.folder = child.key;
140
+ await element.fs.queryPage();
141
+ await element.updateComplete;
142
+
143
+ const back = element.shadowRoot!.querySelector('.back-button') as HTMLElement;
144
+ back.click();
145
+ await oneEvent(element, 'querycomplete');
146
+ await element.updateComplete;
147
+
148
+ assert.equal(element.folder, f1.key, 'switches to the parent folder');
149
+ const f1i = element.shadowRoot!.querySelector(`ui-list-item[data-key="${files.items[0].key}"]`) as UiListItem;
150
+ const f2i = element.shadowRoot!.querySelector(`ui-list-item[data-key="${files.items[1].key}"]`) as UiListItem;
151
+ assert.ok(f1i, 'has the first file');
152
+ assert.ok(f2i, 'has the other file');
153
+
154
+ back.click();
155
+ await oneEvent(element, 'querycomplete');
156
+ await element.updateComplete;
157
+
158
+ assert.isUndefined(element.folder, 'goes up to the root');
159
+ const list = element.shadowRoot!.querySelector('ui-list')!;
160
+ const items = Array.from(list.querySelectorAll('ui-list-item'));
161
+ assert.lengthOf(items, 20, 'renders the list of files');
162
+ });
163
+
164
+ it('render select button disabled', async () => {
165
+ const element = await manualFixture();
166
+ await element.fs.queryPage();
167
+ await element.updateComplete;
168
+
169
+ const button = element.shadowRoot!.querySelector('[value="select"]')!;
170
+ assert.ok(button, 'has the button');
171
+ assert.isTrue(button.hasAttribute('disabled'), 'the button is disabled');
172
+ });
173
+
174
+ it('render cancel button', async () => {
175
+ const element = await manualFixture();
176
+ await element.fs.queryPage();
177
+ await element.updateComplete;
178
+
179
+ const button = element.shadowRoot!.querySelector('[value="cancel"]')!;
180
+ assert.ok(button, 'has the button');
181
+ assert.isFalse(button.hasAttribute('disabled'), 'the button is not disabled');
182
+ });
183
+
184
+ it('selects a file', async () => {
185
+ const element = await manualFixture();
186
+ await element.fs.queryPage();
187
+ await element.updateComplete;
188
+
189
+ const item = element.shadowRoot!.querySelector(`ui-list-item[data-kind="${ProjectKind}"]`) as UiListItem;
190
+ item.click();
191
+
192
+ await element.updateComplete;
193
+ const button = element.shadowRoot!.querySelector('[value="select"]')!;
194
+ assert.isFalse(button.hasAttribute('disabled'), 'the select button is not disabled');
195
+ });
196
+
197
+ it('dispatches the close event with the selected file', async () => {
198
+ const element = await manualFixture();
199
+ await element.fs.queryPage();
200
+ await element.updateComplete;
201
+
202
+ const item = element.shadowRoot!.querySelector(`ui-list-item[data-kind="${ProjectKind}"]`) as UiListItem;
203
+ item.click();
204
+
205
+ await element.updateComplete;
206
+
207
+ const spy = sinon.spy();
208
+ element.addEventListener('close', spy);
209
+ const button = element.shadowRoot!.querySelector('[value="select"]') as HTMLElement;
210
+ button.click();
211
+
212
+ assert.isTrue(spy.calledOnce, 'event is dispatched once');
213
+ const event = spy.args[0][0] as CustomEvent<FilePickerClosingReason>;
214
+ assert.isFalse(event.detail.canceled, 'the dialog was not canceled')
215
+ assert.ok(event.detail.file, 'the detail has the file')
216
+ });
217
+
218
+ it('cancels the dialog', async () => {
219
+ const element = await manualFixture();
220
+ await element.fs.queryPage();
221
+ await element.updateComplete;
222
+
223
+ const item = element.shadowRoot!.querySelector(`ui-list-item[data-kind="${ProjectKind}"]`) as UiListItem;
224
+ item.click();
225
+
226
+ await element.updateComplete;
227
+
228
+ const spy = sinon.spy();
229
+ element.addEventListener('close', spy);
230
+ const button = element.shadowRoot!.querySelector('[value="cancel"]') as HTMLElement;
231
+ button.click();
232
+
233
+ assert.isTrue(spy.calledOnce, 'event is dispatched once');
234
+ const event = spy.args[0][0] as CustomEvent<FilePickerClosingReason>;
235
+ assert.isTrue(event.detail.canceled, 'the dialog was canceled')
236
+ assert.notOk(event.detail.file, 'the detail has no file')
237
+ });
238
+ });
239
+ });
240
+ });
241
+ });
package/test/env.js CHANGED
@@ -6,4 +6,7 @@ export default {
6
6
  issuer: '',
7
7
  tokenProxy: '',
8
8
  },
9
+ store: {
10
+ single: '',
11
+ },
9
12
  };
@@ -314,28 +314,6 @@ describe('events', () => {
314
314
  ensureUnique('EventTypes.Store.History', EventTypes.Store.History);
315
315
  });
316
316
  });
317
-
318
- describe('Certificate namespace', () => {
319
- it('has Certificate namespace', () => {
320
- assert.typeOf(EventTypes.Store.Certificate, 'object');
321
- });
322
-
323
- [
324
- ['read', 'storeclientcertificateread'],
325
- ['list', 'storeclientcertificatelist'],
326
- ['delete', 'storeclientcertificatedelete'],
327
- ['update', 'storeclientcertificateupdate'],
328
- ['insert', 'storeclientcertificateinsert'],
329
- ].forEach(([prop, value]) => {
330
- it(`has ${prop} property`, () => {
331
- assert.equal(EventTypes.Store.Certificate[prop], value);
332
- });
333
- });
334
-
335
- it('has unique events', () => {
336
- ensureUnique('EventTypes.HttpClient.Model.Certificate', EventTypes.Store.Certificate);
337
- });
338
- });
339
317
  });
340
318
 
341
319
  describe('Config', () => {
@@ -0,0 +1,390 @@
1
+ import {
2
+ ApiError, Folder, HttpProject, IFile, IFolder, IHttpProject, IProject, ISpace,
3
+ IUser, PermissionRole, Project, ProjectMock, RouteBuilder, Space, StoreSdk,
4
+ TrashEntry
5
+ } from "@api-client/core/build/browser.js";
6
+ import { WebStoreBindings } from "../../src/bindings/web/WebStoreBindings.js";
7
+ import AppInfo from '../../src/pages/api-client/AppInfo.js';
8
+
9
+ export interface FetchResponse {
10
+ status: number;
11
+ headers: Headers;
12
+ body?: string;
13
+ }
14
+
15
+ export interface HttpOptions extends RequestInit {
16
+ method?: 'GET' | 'POST' | 'PATCH' | 'DELETE';
17
+ headers?: Record<string, string>;
18
+ /**
19
+ * Adds the token to the headers.
20
+ */
21
+ token?: string;
22
+ }
23
+
24
+ export default class HttpHelper {
25
+ user?: IUser;
26
+
27
+ users: IUser[] = [];
28
+
29
+ token?: string;
30
+
31
+ tokens: string[] = [];
32
+
33
+ space?: ISpace;
34
+
35
+ spaces: ISpace[] = [];
36
+
37
+ folder?: IFolder;
38
+
39
+ folders: IFolder[] = [];
40
+
41
+ project?: IProject;
42
+
43
+ projects: IProject[] = [];
44
+
45
+ mocking = new ProjectMock();
46
+
47
+ baseUri: string;
48
+
49
+ store: WebStoreBindings;
50
+
51
+ _sdk?: StoreSdk;
52
+
53
+ get sdk(): StoreSdk {
54
+ const { _sdk } = this;
55
+ if (!_sdk) {
56
+ throw new Error(`Helper not initialized.`);
57
+ }
58
+ return _sdk;
59
+ }
60
+
61
+ constructor(baseUri: string) {
62
+ this.baseUri = baseUri;
63
+ this.store = new WebStoreBindings(AppInfo, baseUri);
64
+ }
65
+
66
+ async initializeSdk(): Promise<void> {
67
+ await this.store.initialize();
68
+ await this.store.global.set({
69
+ key: 'default',
70
+ location: this.baseUri,
71
+ name: 'test env',
72
+ source: 'local-store',
73
+ authenticated: false,
74
+ });
75
+ const authenticated = await this.store.auth.isAuthenticated();
76
+ if (!authenticated) {
77
+ await this.store.auth.authenticate({ updateEnvironment: true });
78
+ }
79
+ this._sdk = this.store.getStore().sdk;
80
+ }
81
+
82
+ findUserToken(user: IUser): string {
83
+ const index = this.users.indexOf(user);
84
+ if (index === -1) {
85
+ throw new ApiError(`Unable to find the token for the user`, 400);
86
+ }
87
+ const token = this.tokens[index];
88
+ if (!token) {
89
+ throw new ApiError(`Invalid state. Token is missing.`, 400);
90
+ }
91
+ return token;
92
+ }
93
+
94
+ async addUser(opts: { defaultUser?: boolean } = {}): Promise<IUser> {
95
+ const token = await this.createUserToken(this.baseUri);
96
+ const user = await this.sdk.user.me({ token });
97
+ this.tokens.push(token);
98
+ this.users.push(user);
99
+ if (opts.defaultUser) {
100
+ this.user = user;
101
+ this.token = token;
102
+ this.sdk.token = token;
103
+ }
104
+ this.users.push(user);
105
+ return user;
106
+ }
107
+
108
+ async addSpace(opts: { defaultSpace?: boolean, user?: IUser } = {}): Promise<ISpace> {
109
+ const { defaultSpace = false, user = this.user } = opts;
110
+ if (!user) {
111
+ throw new ApiError(`User not selected`, 400);
112
+ }
113
+ const token = this.findUserToken(user);
114
+ const space = Space.fromName(this.mocking.lorem.word()).toJSON();
115
+ const record = await this.sdk.spaces.add(space, { token });
116
+ this.spaces.push(record.item!);
117
+ if (defaultSpace) {
118
+ this.space = record.item!;
119
+ this.store.space = record.item!.key;
120
+ }
121
+ return record.item!;
122
+ }
123
+
124
+ async shareSpace(targetUser: IUser, role: PermissionRole, opts: { sourceUser?: IUser, space?: ISpace } = {}): Promise<void> {
125
+ const { sourceUser = this.user, space = this.space } = opts;
126
+ if (!sourceUser) {
127
+ throw new ApiError(`Source user not selected`, 400);
128
+ }
129
+ if (!space) {
130
+ throw new ApiError(`Space not selected`, 400);
131
+ }
132
+ const token = this.findUserToken(sourceUser);
133
+ await this.sdk.spaces.addUser(space.key, targetUser.key, role, undefined, { token });
134
+ }
135
+
136
+ async shareFile(file: IFile, targetUser: IUser, role: PermissionRole, opts: { sourceUser?: IUser, space?: ISpace } = {}): Promise<void> {
137
+ const { sourceUser = this.user, space = this.space } = opts;
138
+ if (!sourceUser) {
139
+ throw new ApiError(`Source user not selected`, 400);
140
+ }
141
+ if (!space) {
142
+ throw new ApiError(`Space not selected`, 400);
143
+ }
144
+ const token = this.findUserToken(sourceUser);
145
+ await this.sdk.file.addUser(space.key, file.key, targetUser.key, role, undefined, { token });
146
+ }
147
+
148
+ async addFolder(opts: { defaultFolder?: boolean, user?: IUser, space?: ISpace, parent?: string } = {}): Promise<IFolder> {
149
+ const { defaultFolder = false, space = this.space, user = this.user, parent } = opts;
150
+ if (!user) {
151
+ throw new ApiError(`User not selected`, 400);
152
+ }
153
+ if (!space) {
154
+ throw new ApiError(`Space not selected`, 400);
155
+ }
156
+ const token = this.findUserToken(user);
157
+ const instance = Folder.fromName(this.mocking.lorem.word(), space.key).toJSON();
158
+ const record = await this.sdk.file.createMeta(instance, space.key, { parent }, { token });
159
+ const folder = record.item as IFolder;
160
+ if (defaultFolder) {
161
+ this.folder = folder;
162
+ }
163
+ this.folders.push(folder);
164
+ return folder;
165
+ }
166
+
167
+ async generateFolders(size = 20, opts: { user?: IUser, space?: ISpace, parent?: string } = {}): Promise<IFolder[]> {
168
+ const { space = this.space, user = this.user } = opts;
169
+ if (!user) {
170
+ throw new ApiError(`User not selected`, 400);
171
+ }
172
+ if (!space) {
173
+ throw new ApiError(`Space not selected`, 400);
174
+ }
175
+ const token = this.findUserToken(user);
176
+ const url = this.getUri('/test/generate/folders');
177
+ url.searchParams.set('size', String(size));
178
+ url.searchParams.set('space', space.key);
179
+ const rsp = await this.post(url.toString(), { token });
180
+ const raw = rsp.body as string;
181
+ const data = JSON.parse(raw) as IFolder[];
182
+ if (!Array.isArray(data)) {
183
+ throw new Error(`Unexpected response.`);
184
+ }
185
+ return data;
186
+ }
187
+
188
+ async addProject(opts: { defaultProject?: boolean, user?: IUser, space?: ISpace, parent?: string } = {}): Promise<IProject> {
189
+ const { parent, space = this.space, user = this.user, defaultProject = false } = opts;
190
+ if (!user) {
191
+ throw new ApiError(`User not selected`, 400);
192
+ }
193
+ if (!space) {
194
+ throw new ApiError(`Space not selected`, 400);
195
+ }
196
+ const token = this.findUserToken(user);
197
+ const project = HttpProject.fromName(this.mocking.lorem.word()).toJSON();
198
+ const file = Project.fromProject(project, space.key).toJSON();
199
+ const record = await this.sdk.file.create(file, project, space.key, { parent }, { token });
200
+ const createdFile = record.item as IProject;
201
+ if (defaultProject) {
202
+ this.project = createdFile;
203
+ }
204
+ this.projects.push(createdFile);
205
+ return createdFile;
206
+ }
207
+
208
+ async generateProjects(size = 20, opts: { user?: IUser, space?: ISpace, parent?: string } = {}): Promise<IHttpProject[]> {
209
+ const { parent, space = this.space, user = this.user } = opts;
210
+ if (!user) {
211
+ throw new ApiError(`User not selected`, 400);
212
+ }
213
+ if (!space) {
214
+ throw new ApiError(`Space not selected`, 400);
215
+ }
216
+ const token = this.findUserToken(user);
217
+ const url = this.getUri('/test/generate/projects');
218
+ url.searchParams.set('size', String(size));
219
+ url.searchParams.set('space', String(space.key));
220
+ url.searchParams.set('owner', user.key);
221
+ if (parent) {
222
+ url.searchParams.set('parent', parent);
223
+ }
224
+ const rsp = await this.post(url.toString(), { token });
225
+ const raw = rsp.body as string;
226
+ const data = JSON.parse(raw) as IHttpProject[];
227
+ if (!Array.isArray(data)) {
228
+ throw new Error(`Unexpected response.`);
229
+ }
230
+ return data;
231
+ }
232
+
233
+ async generateTrash(opts: { size?: number, user?: IUser, space?: ISpace } = {}): Promise<TrashEntry[]> {
234
+ const { space = this.space, user = this.user, size } = opts;
235
+ if (!user) {
236
+ throw new ApiError(`User not selected`, 400);
237
+ }
238
+ if (!space) {
239
+ throw new ApiError(`Space not selected`, 400);
240
+ }
241
+ const token = this.findUserToken(user);
242
+ const url = this.getUri('test/generate/trash');
243
+ url.searchParams.set('owner', user.key);
244
+ url.searchParams.set('space', space.key);
245
+ if (size) {
246
+ url.searchParams.set('size', String(size));
247
+ }
248
+ const rsp = await this.post(url.toString(), { token });
249
+ return JSON.parse(rsp.body!);
250
+ }
251
+
252
+ getUri(path: string): URL {
253
+ let { baseUri } = this;
254
+ if (!baseUri.endsWith('/')) {
255
+ baseUri += '/';
256
+ }
257
+ return new URL(path, baseUri);
258
+ }
259
+
260
+ async get(url: string, opts: HttpOptions = {}): Promise<FetchResponse> {
261
+ const { method = 'GET', headers = {} } = opts;
262
+
263
+ const execHeaders = { ...headers };
264
+ if (opts.token) {
265
+ execHeaders.authorization = `Bearer ${opts.token}`;
266
+ }
267
+ const init: RequestInit = {
268
+ method,
269
+ headers: execHeaders,
270
+ };
271
+ const response = await fetch(url, init)
272
+ const result: FetchResponse = {
273
+ status: response.status,
274
+ headers: response.headers,
275
+ body: await response.text(),
276
+ };
277
+ return result;
278
+ }
279
+
280
+ async post(url: string, opts: HttpOptions = {}): Promise<FetchResponse> {
281
+ const { method = 'POST', headers = {} } = opts;
282
+
283
+ const execHeaders = { ...headers };
284
+ if (opts.token) {
285
+ execHeaders.authorization = `Bearer ${opts.token}`;
286
+ }
287
+ const init: RequestInit = {
288
+ method,
289
+ headers: execHeaders,
290
+ };
291
+ if (opts.body) {
292
+ init.body = opts.body;
293
+ }
294
+ const response = await fetch(url, init)
295
+ const result: FetchResponse = {
296
+ status: response.status,
297
+ headers: response.headers,
298
+ body: await response.text(),
299
+ };
300
+ return result;
301
+ }
302
+
303
+ patch(url: string, opts: HttpOptions = {}): Promise<FetchResponse> {
304
+ const options = { ...opts };
305
+ options.method = 'PATCH';
306
+ return this.post(url, options);
307
+ }
308
+
309
+ delete(url: string, opts: HttpOptions = {}): Promise<FetchResponse> {
310
+ const options = { ...opts };
311
+ options.method = 'DELETE';
312
+ return this.post(url, options);
313
+ }
314
+
315
+ /**
316
+ * Creates unauthenticated session in the backend.
317
+ * @param baseUri Server's base URI (with the prefix).
318
+ * @returns The JWT for unauthenticated user.
319
+ */
320
+ async createSession(baseUri: string): Promise<string> {
321
+ const url = new URL('/sessions', baseUri).toString();
322
+ const result = await this.post(url);
323
+ return result.body as string;
324
+ }
325
+
326
+ /**
327
+ * Initializes the authentication session.
328
+ * @param baseUri Server's base URI (with the prefix).
329
+ * @param token The unauthenticated session JWT.
330
+ * @returns The location of the authorization endpoint.
331
+ */
332
+ async getAuthSessionEndpoint(baseUri: string, token: string): Promise<string> {
333
+ const url = new URL('/auth/login', baseUri).toString();
334
+ const result = await this.post(url, {
335
+ token,
336
+ });
337
+ const loc = result.headers.get('location');
338
+ if (!loc) {
339
+ throw new Error(`The location header not returned by the server.`)
340
+ }
341
+ return loc;
342
+ }
343
+
344
+ /**
345
+ * Performs session initialization and user authentication.
346
+ *
347
+ * @param baseUri The base URI of a multi-user server.
348
+ * @returns The JWT that has authenticated user.
349
+ */
350
+ async createUserToken(baseUri: string): Promise<string> {
351
+ const initToken = await this.createSession(baseUri);
352
+ const meRoute = new URL(RouteBuilder.usersMe(), baseUri).toString();
353
+ const preTest = await this.get(meRoute, {
354
+ token: initToken,
355
+ });
356
+ if (preTest.status === 200) {
357
+ return initToken;
358
+ }
359
+ const path = await this.getAuthSessionEndpoint(baseUri, initToken);
360
+ const authUrl = new URL(`/v1${path}`, baseUri);
361
+ // this test server uses mocked OAuth server which always returns user data.
362
+ await this.get(authUrl.toString());
363
+ // when the above finishes we are either authenticated as a user or not.
364
+ // We gonna check the /users/me endpoint for confirmation.
365
+ const result = await this.get(meRoute, {
366
+ token: initToken,
367
+ });
368
+ // we expect a user info
369
+ if (result.status !== 200) {
370
+ throw new Error(`Authentication unsuccessful. Reported status for ${RouteBuilder.usersMe()}: ${result.status}`);
371
+ }
372
+ return initToken;
373
+ }
374
+
375
+ async clearFiles(): Promise<void> {
376
+ await this.delete(`${this.baseUri}/test/reset/files`);
377
+ }
378
+
379
+ async clearSpaces(): Promise<void> {
380
+ await this.delete(`${this.baseUri}/test/reset/spaces`);
381
+ }
382
+
383
+ async clearPermissions(): Promise<void> {
384
+ await this.delete(`${this.baseUri}/test/reset/permissions`);
385
+ }
386
+
387
+ async clearSessions(): Promise<void> {
388
+ await this.delete(`${this.baseUri}/test/reset/sessions`);
389
+ }
390
+ }
@@ -39,8 +39,8 @@ export class UiMock {
39
39
  const { x, y, width, height } = element.getBoundingClientRect();
40
40
 
41
41
  return {
42
- x: Math.floor(x + window.pageXOffset + width / 2),
43
- y: Math.floor(y + window.pageYOffset + height / 2),
42
+ x: Math.floor(x + window.screenX + width / 2),
43
+ y: Math.floor(y + window.scrollY + height / 2),
44
44
  };
45
45
  }
46
46
 
@@ -96,4 +96,21 @@ export class UiMock {
96
96
  await sendMouse({ type: 'up' });
97
97
  // await executeServerCommand('take-screenshot', { name: `after-up` });
98
98
  }
99
+
100
+ static getFileDragEvent(type: string, opts: { file?: File } = {}): DragEvent {
101
+ let file: File;
102
+ if (opts.file) {
103
+ file = opts.file;
104
+ } else {
105
+ file = new File(['test'], 'test.txt', { type: 'text/plain' });
106
+ }
107
+ const dt = new DataTransfer();
108
+ dt.items.add(file);
109
+ return new DragEvent(type, {
110
+ dataTransfer: dt,
111
+ cancelable: true,
112
+ composed: true,
113
+ bubbles: true
114
+ });
115
+ }
99
116
  }