glib-web 5.0.7 → 6.0.0

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 (56) hide show
  1. package/.claude/commands/gen-cypress-test.md +101 -0
  2. package/.github/dependabot.yml +24 -12
  3. package/.nycrc.json +4 -0
  4. package/AGENTS.md +1 -0
  5. package/README.md +4 -0
  6. package/actions/logics/set.js +5 -0
  7. package/actions/panels/scrollTo.js +2 -2
  8. package/actions/timeouts/set.js +1 -1
  9. package/app.vue +1 -1
  10. package/components/charts/series.js +2 -2
  11. package/components/composable/gmap.js +1 -1
  12. package/components/composable/upload.js +1 -4
  13. package/components/composable/upload_nothing.js +1 -1
  14. package/components/fields/_buttonDate.vue +2 -2
  15. package/components/fields/_patternText.vue +2 -8
  16. package/components/fields/_select.vue +10 -8
  17. package/components/fields/_selectItemDefault.vue +1 -3
  18. package/components/fields/_selectItemWithIcon.vue +1 -3
  19. package/components/fields/_selectItemWithImage.vue +1 -3
  20. package/components/fields/clipboardUpload.vue +115 -0
  21. package/components/fields/dynamicGroup2.vue +1 -1
  22. package/components/fields/multiUpload.vue +12 -19
  23. package/components/fields/placeholderUpload.vue +8 -4
  24. package/components/fields/radioGroup.vue +0 -7
  25. package/components/fields/sign.vue +1 -1
  26. package/components/fields/text.vue +1 -9
  27. package/components/fields/upload.vue +4 -0
  28. package/components/mixins/longClick.js +1 -1
  29. package/components/panels/bulkEdit2.vue +1 -1
  30. package/components/panels/list.vue +1 -1
  31. package/components/popover.vue +1 -1
  32. package/components/responsive.vue +1 -1
  33. package/cypress/e2e/glib-web/dialog.cy.js +0 -1
  34. package/cypress/e2e/glib-web/dirtyState.cy.js +37 -37
  35. package/cypress/e2e/glib-web/fieldsDateTime.cy.js +46 -3
  36. package/cypress/e2e/glib-web/fieldsRadio.cy.js +65 -0
  37. package/cypress/e2e/glib-web/fieldsSelect.cy.js +116 -76
  38. package/cypress/e2e/glib-web/fieldsText.cy.js +83 -0
  39. package/cypress/e2e/glib-web/fieldsUpload.cy.js +230 -120
  40. package/cypress/e2e/glib-web/image.cy.js +62 -33
  41. package/cypress/e2e/glib-web/switch.cy.js +13 -0
  42. package/cypress/e2e/glib-web/tabBar.cy.js +23 -0
  43. package/cypress/fixtures/document.pdf +12 -0
  44. package/cypress/fixtures/large.png +0 -0
  45. package/cypress/fixtures/upload.png +0 -0
  46. package/cypress/helper.js +13 -1
  47. package/cypress/support/component.js +1 -1
  48. package/cypress/support/e2e.js +20 -13
  49. package/doc/dependabot.md +22 -0
  50. package/eslint-rules/index.js +6 -6
  51. package/nav/dialog.vue +1 -1
  52. package/package.json +18 -16
  53. package/templates/_menu.vue +2 -2
  54. package/cypress/component/inputUpload.cy.js +0 -103
  55. package/cypress/component/multiUpload.cy.js +0 -107
  56. package/cypress/component/placeholderUpload.cy.js +0 -91
@@ -0,0 +1,83 @@
1
+ import { testPageUrl } from "../../helper.js";
2
+
3
+ const url = testPageUrl('fields_text');
4
+
5
+ describe('fields_text', () => {
6
+ it('shows status changed when Name field is edited', () => {
7
+ cy.visit(url);
8
+
9
+ cy.contains('Status: idle').should('exist');
10
+ cy.get('input[placeholder="Full name"]').type('John Doe');
11
+ cy.contains('Status: changed').should('exist');
12
+ });
13
+
14
+ it('Set value fills the Name field with Hello world', () => {
15
+ cy.visit(url);
16
+
17
+ cy.contains('Set value').click();
18
+ cy.get('input[placeholder="Full name"]').should('have.value', 'Hello world');
19
+ });
20
+
21
+ it('Clear value empties the Name field', () => {
22
+ cy.visit(url);
23
+
24
+ cy.contains('Set value').click();
25
+ cy.get('input[placeholder="Full name"]').should('have.value', 'Hello world');
26
+ cy.contains('Clear value').click();
27
+ cy.get('input[placeholder="Full name"]').should('have.value', '');
28
+ });
29
+
30
+ it('Focus button focuses the Name field', () => {
31
+ cy.visit(url);
32
+
33
+ cy.contains('Focus').click();
34
+ cy.get('input[placeholder="Full name"]').should('be.focused');
35
+ });
36
+
37
+ it('Reset button resets fields and shows status reset', () => {
38
+ cy.visit(url);
39
+
40
+ cy.get('input[placeholder="Full name"]').type('Changed text');
41
+ cy.contains('Reset').click();
42
+ cy.get('#text_status').scrollIntoView().should('contain', 'Status: reset');
43
+ cy.get('input[placeholder="Full name"]').should('have.value', '');
44
+ });
45
+
46
+ it('password visibility toggle reveals and hides password text', () => {
47
+ cy.visit(url);
48
+
49
+ cy.get('input[placeholder="Enter your password"]').type('secret123');
50
+ cy.get('input[placeholder="Enter your password"]').should('have.attr', 'type', 'password');
51
+ cy.get('input[placeholder="Enter your password"]')
52
+ .closest('.v-input')
53
+ .find('i.material-icons[role="button"]')
54
+ .click();
55
+ cy.get('input[placeholder="Enter your password"]').should('have.attr', 'type', 'text');
56
+ });
57
+
58
+ it('derived value updates when Base value changes', () => {
59
+ cy.visit(url);
60
+
61
+ cy.contains('Base value').closest('.v-input').find('input').clear().type('30');
62
+ cy.get('input[name="user[derived_value]"]').should('have.value', '10');
63
+ });
64
+
65
+ it('password strength hint appears when password reaches 8 characters', () => {
66
+ cy.visit(url);
67
+
68
+ cy.get('input[placeholder="Type a password..."]').type('short');
69
+ cy.contains('Password length looks good').should('not.be.visible');
70
+ cy.get('input[placeholder="Type a password..."]').type('longpass');
71
+ cy.contains('Password length looks good').should('be.visible');
72
+ });
73
+
74
+ it('Submit opens a dialog showing POST form data', () => {
75
+ cy.visit(url);
76
+
77
+ cy.contains('Submit').click();
78
+ cy.contains('Method: POST').should('be.visible');
79
+ cy.contains('Form Data:').should('be.visible');
80
+ cy.contains('OK').click();
81
+ cy.contains('Method: POST').should('not.exist');
82
+ });
83
+ });
@@ -1,57 +1,30 @@
1
- import { testPageUrl } from "../../helper.js"
1
+ import { testPageUrl, withComponent, withComponentThen } from "../../helper.js";
2
2
 
3
- const url = testPageUrl('fields_upload')
4
-
5
- const getRealComponent = (win, component) => {
6
- const { isObject, isNotNull } = win.Utils.type
7
- let realComp = component
8
-
9
- while (isObject(realComp) && isNotNull(realComp.$refs?.delegate)) {
10
- realComp = realComp.$refs.delegate
11
- }
12
-
13
- return realComp
14
- }
3
+ const url = testPageUrl('fields_upload');
15
4
 
16
5
  describe('fields_upload', () => {
17
- it('invokes upload triggers and resets', () => {
18
- cy.visit(url)
19
-
20
- cy.window().then((win) => {
21
- const basicComp = getRealComponent(win, win.GLib.component.findById('fields_upload_basic'))
22
- const filesComp = getRealComponent(win, win.GLib.component.findById('fields_upload_files'))
23
-
24
- cy.wrap(basicComp).should('exist')
25
- cy.wrap(filesComp).should('exist')
26
6
 
27
- cy.spy(basicComp, 'trigger').as('basicTrigger')
28
- cy.spy(basicComp, 'reset').as('basicReset')
29
- cy.spy(filesComp, 'trigger').as('filesTrigger')
30
- })
7
+ it('resets the avatar placeholderView field value', () => {
8
+ cy.visit(url);
31
9
 
32
- cy.contains('Trigger').click()
33
- cy.get('@basicTrigger').should('have.been.calledOnce')
10
+ withComponent('fields_upload_avatar', (comp) => cy.spy(comp, 'reset').as('avatarReset'));
34
11
 
35
- cy.contains('Reset').click()
36
- cy.get('@basicReset').should('have.been.calledOnce')
12
+ cy.get('.v-btn').filter(':contains("Reset")').eq(1).click();
13
+ cy.get('@avatarReset').should('have.been.calledOnce');
37
14
 
38
- cy.contains('Select file').click()
39
- cy.get('@filesTrigger').should('have.been.calledOnce')
40
- })
15
+ cy.contains('submit').click();
16
+ cy.get('.unformatted').should('contain.text', 'Method: POST').and('not.contain.text', '"file_avatar": "test"');
17
+ cy.get('.v-dialog').contains('OK').click();
18
+ });
41
19
 
42
20
  it('handles multi progress view drag and drop uploads', () => {
43
- const htmlUploadUrl = 'http://localhost:3000/glib/json_ui_garage?path=forms%2Ffile_upload_new&mode=html'
44
- let uploadIndex = 0
45
- const pngBase64 =
46
- 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGNgYAAAAAMAASsJTYQAAAAASUVORK5CYII='
47
- const pngFile = {
48
- contents: Cypress.Buffer.from(pngBase64, 'base64'),
49
- fileName: 'upload.png',
50
- mimeType: 'image/png'
51
- }
21
+ const htmlUploadUrl = 'http://localhost:3000/glib/json_ui_garage?path=forms%2Ffile_upload_new&mode=html';
22
+ let uploadIndex = 0;
23
+
24
+ cy.fixture('upload.png', null).as('pngContents');
52
25
 
53
26
  cy.intercept('POST', /\/(glib\/glib_direct_uploads|rails\/active_storage\/direct_uploads)$/, (req) => {
54
- uploadIndex += 1
27
+ uploadIndex += 1;
55
28
  req.reply({
56
29
  statusCode: 200,
57
30
  body: {
@@ -63,97 +36,234 @@ describe('fields_upload', () => {
63
36
  }
64
37
  }
65
38
  }
66
- })
67
- }).as('directUploadCreate')
39
+ });
40
+ }).as('directUploadCreate');
68
41
 
69
42
  cy.intercept('PUT', 'https://example.com/rails/active_storage/blobs/upload', {
70
43
  statusCode: 200
71
- }).as('directUploadStore')
72
-
73
- cy.visit(url)
74
-
75
- cy.window()
76
- .should((win) => {
77
- const multiComp = getRealComponent(win, win.GLib.component.findById('fields_upload_multiprogress'))
78
- expect(multiComp, 'multi progress component').to.exist
79
- expect(multiComp.$el, 'multi progress element').to.exist
80
- expect(multiComp.$el.querySelector('.gdrop-file'), 'multi progress dropzone').to.exist
81
- })
82
- .then((win) => {
83
- const multiComp = getRealComponent(win, win.GLib.component.findById('fields_upload_multiprogress'))
84
- cy.wrap(multiComp.$el).as('multiRoot')
85
- cy.wrap(multiComp.$el.querySelector('.gdrop-file')).as('dropzone')
86
- })
87
-
88
- cy.get('@dropzone').should('have.class', 'border-[2px]')
89
- cy.get('@dropzone').trigger('drag')
90
- cy.get('@dropzone').trigger('dragover')
91
- cy.get('@dropzone').should('have.class', 'border-[4px]').and('not.have.class', 'border-[2px]')
92
- cy.get('@dropzone').trigger('dragleave')
93
- cy.get('@dropzone').should('have.class', 'border-[2px]')
94
-
95
- cy.get('@dropzone').click({ force: true })
96
- cy.get('@multiRoot').find('input[type="file"]').should('exist')
97
-
98
- cy.get('@dropzone').selectFile(
99
- pngFile,
100
- { action: 'drag-drop' }
101
- )
102
-
103
- cy.wait('@directUploadCreate')
104
- cy.wait('@directUploadStore')
44
+ }).as('directUploadStore');
45
+
46
+ cy.visit(url);
47
+
48
+ withComponentThen('fields_upload_multiprogress', (comp) => {
49
+ expect(comp.$el, 'multi progress element').to.exist;
50
+ expect(comp.$el.querySelector('.gdrop-file'), 'multi progress dropzone').to.exist;
51
+ cy.wrap(comp.$el).as('multiRoot');
52
+ cy.wrap(comp.$el.querySelector('.gdrop-file')).as('dropzone');
53
+ });
54
+
55
+ cy.get('@dropzone').should('have.class', 'border-[2px]');
56
+ cy.get('@dropzone').trigger('drag');
57
+ cy.get('@dropzone').trigger('dragover');
58
+ cy.get('@dropzone').should('have.class', 'border-[4px]').and('not.have.class', 'border-[2px]');
59
+ cy.get('@dropzone').trigger('dragleave');
60
+ cy.get('@dropzone').should('have.class', 'border-[2px]');
61
+
62
+ cy.get('@dropzone').click({ force: true });
63
+ cy.get('@multiRoot').find('input[type="file"]').should('exist');
64
+
65
+ cy.get('@pngContents').then((contents) => {
66
+ cy.get('@dropzone').selectFile(
67
+ { contents, fileName: 'upload.png', mimeType: 'image/png' },
68
+ { action: 'drag-drop' }
69
+ );
70
+ });
71
+
72
+ cy.wait('@directUploadCreate');
73
+ cy.wait('@directUploadStore');
105
74
  cy.get('@multiRoot')
106
75
  .find('input[type="hidden"][name="user[file_input_multiprogress][]"]')
107
- .should('have.length', 1)
76
+ .should('have.length', 1);
108
77
 
109
- cy.get('@dropzone').selectFile(
110
- { ...pngFile, fileName: 'second.png' },
111
- { action: 'drag-drop' }
112
- )
78
+ cy.get('@pngContents').then((contents) => {
79
+ cy.get('@dropzone').selectFile(
80
+ { contents, fileName: 'second.png', mimeType: 'image/png' },
81
+ { action: 'drag-drop' }
82
+ );
83
+ });
113
84
 
114
- cy.wait('@directUploadCreate')
115
- cy.wait('@directUploadStore')
85
+ cy.wait('@directUploadCreate');
86
+ cy.wait('@directUploadStore');
116
87
 
117
- cy.get('@multiRoot').find('.guploaded-file .file-container').should('have.length', 2)
88
+ cy.get('@multiRoot').find('.guploaded-file .file-container').should('have.length', 2);
118
89
  cy.get('@multiRoot')
119
90
  .find('input[type="hidden"][name="user[file_input_multiprogress][]"]')
120
- .should('have.length', 2)
121
- cy.get('@multiRoot').find('.guploaded-file .close-btn').first().click()
122
- cy.get('@multiRoot').find('.guploaded-file .file-container').should('have.length', 1)
91
+ .should('have.length', 2);
92
+ cy.get('@multiRoot').find('.guploaded-file .close-btn').first().click();
93
+ cy.get('@multiRoot').find('.guploaded-file .file-container').should('have.length', 1);
123
94
  cy.get('@multiRoot')
124
95
  .find('input[type="hidden"][name="user[file_input_multiprogress][]"]')
125
- .should('have.length', 1)
126
-
127
- cy.visit(htmlUploadUrl)
128
-
129
- cy.window()
130
- .should((win) => {
131
- const multiComp = getRealComponent(win, win.GLib.component.findById('mp1'))
132
- expect(multiComp, 'html multi progress component').to.exist
133
- expect(multiComp.$el, 'html multi progress element').to.exist
134
- expect(multiComp.$el.querySelector('.gdrop-file'), 'html multi progress dropzone').to.exist
135
- })
136
- .then((win) => {
137
- const multiComp = getRealComponent(win, win.GLib.component.findById('mp1'))
138
- cy.wrap(multiComp.$el).as('htmlMultiRoot')
139
- cy.wrap(multiComp.$el.querySelector('.gdrop-file')).as('htmlDropzone')
140
- })
141
-
142
- cy.get('@htmlDropzone').selectFile(
143
- { ...pngFile, fileName: 'html-mode.png' },
144
- { action: 'drag-drop' }
145
- )
146
-
147
- cy.get('@htmlMultiRoot').find('.guploaded-file .file-container').should('have.length', 1)
96
+ .should('have.length', 1);
97
+
98
+ cy.visit(htmlUploadUrl);
99
+
100
+ withComponentThen('mp1', (comp) => {
101
+ expect(comp.$el, 'html multi progress element').to.exist;
102
+ expect(comp.$el.querySelector('.gdrop-file'), 'html multi progress dropzone').to.exist;
103
+ cy.wrap(comp.$el).as('htmlMultiRoot');
104
+ cy.wrap(comp.$el.querySelector('.gdrop-file')).as('htmlDropzone');
105
+ });
106
+
107
+ cy.get('@pngContents').then((contents) => {
108
+ cy.get('@htmlDropzone').selectFile(
109
+ { contents, fileName: 'html-mode.png', mimeType: 'image/png' },
110
+ { action: 'drag-drop' }
111
+ );
112
+ });
113
+
114
+ cy.get('@htmlMultiRoot').find('.guploaded-file .file-container').should('have.length', 1);
148
115
  cy.get('@htmlMultiRoot')
149
116
  .find('input[type="hidden"][name="user[file_multiprogress1][]"]')
150
- .should('not.exist')
151
- })
117
+ .should('not.exist');
118
+ });
119
+
120
+ it('rejects files with an invalid type in multi progress view', () => {
121
+ cy.fixture('document.pdf', null).as('pdfContents');
122
+
123
+ cy.visit(url);
124
+
125
+ withComponentThen('fields_upload_multiprogress', (comp) => {
126
+ expect(comp.$el.querySelector('.gdrop-file'), 'dropzone').to.exist;
127
+ cy.wrap(comp.$el).as('multiRoot');
128
+ cy.wrap(comp.$el.querySelector('.gdrop-file')).as('dropzone');
129
+ });
130
+
131
+ cy.get('@pdfContents').then((contents) => {
132
+ cy.get('@dropzone').selectFile(
133
+ { contents, fileName: 'document.pdf', mimeType: 'application/pdf' },
134
+ { action: 'drag-drop' }
135
+ );
136
+ });
137
+ cy.get('@multiRoot').find('.guploaded-file').should('not.exist');
138
+ });
139
+
140
+ it('rejects files that exceed the maximum file size in multi progress view', () => {
141
+ // maxFileSize: 10 in the test page → 10 * 1000 = 10,000 bytes limit
142
+ cy.fixture('large.png', null).as('largeContents');
143
+
144
+ cy.visit(url);
145
+
146
+ withComponentThen('fields_upload_multiprogress', (comp) => {
147
+ expect(comp.$el.querySelector('.gdrop-file'), 'dropzone').to.exist;
148
+ cy.wrap(comp.$el).as('multiRoot');
149
+ cy.wrap(comp.$el.querySelector('.gdrop-file')).as('dropzone');
150
+ });
151
+
152
+ cy.get('@largeContents').then((contents) => {
153
+ cy.get('@dropzone').selectFile(
154
+ { contents, fileName: 'large.png', mimeType: 'image/png' },
155
+ { action: 'drag-drop' }
156
+ );
157
+ });
158
+ cy.get('@multiRoot').find('.guploaded-file').should('not.exist');
159
+ });
160
+
161
+ it('rejects uploads that exceed the maximum file count in multi progress view', () => {
162
+ let uploadIndex = 0;
163
+
164
+ cy.fixture('upload.png', null).as('pngContents');
165
+
166
+ cy.intercept('POST', /\/(glib\/glib_direct_uploads|rails\/active_storage\/direct_uploads)$/, (req) => {
167
+ uploadIndex += 1;
168
+ req.reply({
169
+ statusCode: 200,
170
+ body: {
171
+ signed_id: `signed-${uploadIndex}`,
172
+ direct_upload: {
173
+ url: 'https://example.com/rails/active_storage/blobs/upload',
174
+ headers: { 'Content-Type': 'text/plain' }
175
+ }
176
+ }
177
+ });
178
+ }).as('directUploadCreate');
179
+
180
+ cy.intercept('PUT', 'https://example.com/rails/active_storage/blobs/upload', { statusCode: 200 }).as('directUploadStore');
181
+
182
+ cy.visit(url);
183
+
184
+ withComponentThen('fields_upload_multiprogress', (comp) => {
185
+ expect(comp.$el.querySelector('.gdrop-file'), 'dropzone').to.exist;
186
+ cy.wrap(comp.$el).as('multiRoot');
187
+ cy.wrap(comp.$el.querySelector('.gdrop-file')).as('dropzone');
188
+ });
189
+
190
+ // Upload 2 files to reach maxFileLength: 2
191
+ cy.get('@pngContents').then((contents) => {
192
+ cy.get('@dropzone').selectFile(
193
+ { contents, fileName: 'first.png', mimeType: 'image/png' },
194
+ { action: 'drag-drop' }
195
+ );
196
+ });
197
+ cy.wait('@directUploadCreate');
198
+ cy.wait('@directUploadStore');
199
+ cy.get('@pngContents').then((contents) => {
200
+ cy.get('@dropzone').selectFile(
201
+ { contents, fileName: 'second.png', mimeType: 'image/png' },
202
+ { action: 'drag-drop' }
203
+ );
204
+ });
205
+ cy.wait('@directUploadCreate');
206
+ cy.wait('@directUploadStore');
207
+ cy.get('@multiRoot').find('.guploaded-file .file-container').should('have.length', 2);
208
+
209
+ // A 3rd file should be rejected — container count stays at 2
210
+ cy.get('@pngContents').then((contents) => {
211
+ cy.get('@dropzone').selectFile(
212
+ { contents, fileName: 'third.png', mimeType: 'image/png' },
213
+ { action: 'drag-drop' }
214
+ );
215
+ });
216
+ cy.get('@multiRoot').find('.guploaded-file .file-container').should('have.length', 2);
217
+ });
152
218
 
153
219
  it('submits the form', () => {
154
- cy.visit(url)
220
+ cy.visit(url);
221
+
222
+ cy.contains('submit').click();
223
+ cy.get('.unformatted').should('contain.text', 'Method: POST');
224
+ });
225
+
226
+ it('pastes an image from clipboard and shows filename', () => {
227
+ let uploadIndex = 0;
228
+
229
+ cy.intercept('POST', /\/(glib\/glib_direct_uploads|rails\/active_storage\/direct_uploads)$/, (req) => {
230
+ uploadIndex += 1;
231
+ req.reply({
232
+ statusCode: 200,
233
+ body: {
234
+ signed_id: `signed-clip-${uploadIndex}`,
235
+ direct_upload: {
236
+ url: 'https://example.com/rails/active_storage/blobs/upload',
237
+ headers: { 'Content-Type': 'text/plain' }
238
+ }
239
+ }
240
+ });
241
+ }).as('directUploadCreate');
242
+
243
+ cy.intercept('PUT', 'https://example.com/rails/active_storage/blobs/upload', {
244
+ statusCode: 200
245
+ }).as('directUploadStore');
246
+
247
+ cy.visit(url);
248
+
249
+ withComponentThen('fields_upload_clipboard', (comp) => {
250
+ cy.wrap(comp.$el).as('clipRoot');
251
+ });
252
+
253
+ cy.window().then((win) => {
254
+ const mockBlob = new win.Blob(['fake-png'], { type: 'image/png' });
255
+ cy.stub(win.navigator.clipboard, 'read').resolves([
256
+ { types: ['image/png'], getType: () => Promise.resolve(mockBlob) }
257
+ ]);
258
+ });
259
+
260
+ cy.get('@clipRoot').find('.v-input__append i').click();
261
+ cy.wait('@directUploadCreate');
262
+ cy.wait('@directUploadStore');
155
263
 
156
- cy.contains('submit').click()
157
- cy.get('.unformatted').should('contain.text', 'Method: POST')
158
- })
159
- })
264
+ cy.get('@clipRoot').find('input').should('have.value', 'clipboard.png');
265
+ cy.get('@clipRoot')
266
+ .find('input[type="hidden"][name="user[file_clipboard]"]')
267
+ .should('have.value', 'signed-clip-1');
268
+ });
269
+ });
@@ -1,53 +1,82 @@
1
- import { testPageUrl } from "../../helper.js"
1
+ import { testPageUrl } from "../../helper.js";
2
2
 
3
- const url = testPageUrl('image')
4
- const imageSelector = '#image_main'
3
+ const url = testPageUrl('image');
4
+ const imageSelector = '#image_main';
5
5
 
6
6
  const getImage = () => {
7
7
  return cy.get(imageSelector).then(($el) => {
8
- const image = $el.is('img') ? $el : $el.find('img')
9
- expect(image.length).to.be.greaterThan(0)
10
- return cy.wrap(image)
11
- })
12
- }
8
+ const image = $el.is('img') ? $el : $el.find('img');
9
+ expect(image.length).to.be.greaterThan(0);
10
+ return cy.wrap(image);
11
+ });
12
+ };
13
13
 
14
14
  describe('image', () => {
15
15
  it('updates size and fit settings', () => {
16
- cy.visit(url)
16
+ cy.visit(url);
17
17
 
18
- cy.contains('Default').click()
19
- getImage().should('have.css', 'object-fit', 'contain')
18
+ cy.contains('Default').click();
19
+ getImage().should('have.css', 'object-fit', 'contain');
20
20
 
21
- cy.contains('Crop square').click()
22
- getImage().should('have.css', 'object-fit', 'cover')
21
+ cy.contains('Crop square').click();
22
+ getImage().should('have.css', 'object-fit', 'cover');
23
23
  getImage().should(($image) => {
24
- expect($image.attr('width')).to.eq('160px')
25
- })
24
+ expect($image.attr('width')).to.eq('160px');
25
+ });
26
26
 
27
- cy.contains('Full width').click()
28
- getImage().should('have.css', 'object-fit', 'contain')
27
+ cy.contains('Full width').click();
28
+ getImage().should('have.css', 'object-fit', 'contain');
29
29
 
30
- cy.contains('Tall crop').click()
31
- getImage().should('have.css', 'object-fit', 'cover')
32
- })
30
+ cy.contains('Tall crop').click();
31
+ getImage().should('have.css', 'object-fit', 'cover');
32
+ });
33
33
 
34
34
  it('toggles badges', () => {
35
- cy.visit(url)
35
+ cy.visit(url);
36
36
 
37
- cy.contains('Show badge').click()
38
- cy.get('#image_main .v-badge__badge').should('contain.text', '3')
39
- })
37
+ cy.contains('Show badge').click();
38
+ cy.get('#image_main .v-badge__badge').should('contain.text', '3');
39
+ });
40
40
 
41
41
  it('handles tiny thumbnails', () => {
42
- cy.visit(url)
42
+ cy.visit(url);
43
43
 
44
- cy.contains('Tiny thumbnail').click()
45
- getImage().should('have.css', 'object-fit', 'cover')
44
+ cy.contains('Tiny thumbnail').click();
45
+ getImage().should('have.css', 'object-fit', 'cover');
46
46
  getImage().should(($image) => {
47
- expect($image.attr('width')).to.eq('64px')
48
- })
47
+ expect($image.attr('width')).to.eq('64px');
48
+ });
49
49
 
50
- cy.contains('Restore size').click()
51
- getImage().should('have.css', 'object-fit', 'contain')
52
- })
53
- })
50
+ cy.contains('Restore size').click();
51
+ getImage().should('have.css', 'object-fit', 'contain');
52
+ });
53
+
54
+ it('fits by height only (clip)', () => {
55
+ cy.visit(url);
56
+
57
+ cy.contains('Height clip').click();
58
+ getImage().should('have.css', 'object-fit', 'contain');
59
+ getImage().should(($image) => {
60
+ expect($image.attr('height')).to.eq('200px');
61
+ });
62
+ });
63
+
64
+ it('fits by height only (crop)', () => {
65
+ cy.visit(url);
66
+
67
+ cy.contains('Height crop').click();
68
+ getImage().should('have.css', 'object-fit', 'cover');
69
+ getImage().should(($image) => {
70
+ expect($image.attr('height')).to.eq('160px');
71
+ });
72
+ });
73
+
74
+ it('handles image onClick', () => {
75
+ cy.visit(url);
76
+
77
+ cy.on('window:alert', (message) => {
78
+ expect(message).to.include('Image clicked');
79
+ });
80
+ getImage().click();
81
+ });
82
+ });
@@ -0,0 +1,13 @@
1
+ import { testPageUrl } from "../../helper.js";
2
+
3
+ const url = testPageUrl('switch');
4
+
5
+ describe('switch', () => {
6
+ it('toggles push notifications on from default off state', () => {
7
+ cy.visit(url);
8
+
9
+ cy.contains('Push notifications').click();
10
+
11
+ cy.contains('Push enabled').should('be.visible');
12
+ });
13
+ });
@@ -0,0 +1,23 @@
1
+ import { testPageUrl } from "../../helper.js";
2
+
3
+ const url = testPageUrl('tabBar');
4
+
5
+ describe('tabBar', () => {
6
+ it('shows Tapped Alerts snackbar when Alerts tab is clicked', () => {
7
+ cy.visit(url);
8
+
9
+ cy.contains('Alerts').click();
10
+
11
+ cy.contains('Tapped Alerts').should('be.visible');
12
+ });
13
+
14
+ it('renders all-disabled tabs as unclickable', () => {
15
+ cy.visit(url);
16
+
17
+ cy.contains('All tabs disabled').should('be.visible');
18
+ cy.contains('One').should('be.visible');
19
+ cy.contains('Two').should('be.visible');
20
+ cy.contains('Three').should('be.visible');
21
+ });
22
+
23
+ });
@@ -0,0 +1,12 @@
1
+ %PDF-1.4
2
+ 1 0 obj<</Type /Catalog /Pages 2 0 R>>endobj 2 0 obj<</Type /Pages /Kids [3 0 R] /Count 1>>endobj 3 0 obj<</Type /Page /MediaBox [0 0 3 3]>>endobj
3
+ xref
4
+ 0 4
5
+ 0000000000 65535 f
6
+ 0000000009 00000 n
7
+ 0000000058 00000 n
8
+ 0000000115 00000 n
9
+ trailer<</Size 4 /Root 1 0 R>>
10
+ startxref
11
+ 190
12
+ %%EOF
Binary file
Binary file
package/cypress/helper.js CHANGED
@@ -16,4 +16,16 @@ function withComponent(id, callback) {
16
16
  })
17
17
  }
18
18
 
19
- export { testPageUrl, withComponent }
19
+ function withComponentThen(id, callback) {
20
+ return cy.window()
21
+ .should((win) => {
22
+ const comp = realComponent(win.GLib.component.findById(id))
23
+ expect(comp).to.exist
24
+ expect(comp.$el instanceof win.HTMLElement).to.be.true
25
+ })
26
+ .then((win) => {
27
+ callback(realComponent(win.GLib.component.findById(id)))
28
+ })
29
+ }
30
+
31
+ export { testPageUrl, withComponent, withComponentThen }