Skip to content

Commit a9c3b5a

Browse files
view toolbar: add component tests
1 parent ab19155 commit a9c3b5a

File tree

4 files changed

+320
-4
lines changed

4 files changed

+320
-4
lines changed

.eslintrc.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ module.exports = {
1919
root: true,
2020
env: {
2121
node: true
22+
// 'cypress/globals': true
2223
},
2324
extends: [
2425
'standard',
2526
'eslint:recommended',
26-
'plugin:vue/essential'
27+
'plugin:vue/essential',
28+
'plugin:cypress/recommended'
2729
],
2830
rules: {
2931
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
/**
2+
* Copyright (C) NIWA & British Crown (Met Office) & Contributors.
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16+
*/
17+
18+
import {
19+
mdiCog,
20+
mdiGestureTap,
21+
mdiToggleSwitch
22+
} from '@mdi/js'
23+
import ViewToolbar from '@/components/cylc/ViewToolbar'
24+
25+
describe('View Toolbar Component', () => {
26+
const mountToolbar = (groups) => {
27+
// configure a listener for toggle events
28+
const setOption = cy.spy().as('setOptionSpy')
29+
30+
// mount 'em
31+
cy.vmount(
32+
ViewToolbar,
33+
{
34+
propsData: { groups },
35+
listeners: { setOption }
36+
}
37+
)
38+
// add the classes Vuetify requires
39+
cy.addVuetifyStyles(cy)
40+
41+
return setOption
42+
}
43+
44+
it('loads, toggles and runs callbacks ', () => {
45+
// set up a handler for callback events
46+
const callbacks = []
47+
function myCallback () {
48+
callbacks.push(true)
49+
}
50+
51+
// mount the toolbar with a couple of controls
52+
mountToolbar([
53+
{
54+
title: 'Group 1',
55+
controls: [
56+
{
57+
title: 'Toggle',
58+
icon: mdiToggleSwitch,
59+
action: 'toggle',
60+
value: true,
61+
key: 'toggle'
62+
},
63+
{
64+
title: 'Callback',
65+
icon: mdiGestureTap,
66+
action: 'callback',
67+
callback: myCallback,
68+
key: 'callback'
69+
}
70+
]
71+
}
72+
])
73+
74+
// test all controls rendered
75+
cy
76+
.get('.group')
77+
.should('have.length', 1)
78+
.get('.control')
79+
.should('have.length', 2)
80+
.should('be.visible')
81+
82+
// test action=toggle
83+
cy
84+
// the toggle should be blue because it's active (default true)
85+
.get('.control.toggle .v-icon')
86+
.should('have.class', 'blue--text')
87+
// clicking the toggle should emit a "setOption" event with the
88+
// controls key (toggle) and new value (false)
89+
.click({ force: true })
90+
.get('@setOptionSpy')
91+
.should('have.been.calledWith', 'toggle', false)
92+
// it should not be grey because it's inactive (false)
93+
.get('.control.toggle .v-icon')
94+
.not('.blue--text')
95+
// click it again and it should become active again
96+
.click({ force: true })
97+
.get('@setOptionSpy')
98+
.should('have.been.calledWith', 'toggle', true)
99+
.get('.control.toggle .v-icon')
100+
.should('have.class', 'blue--text')
101+
102+
// test action=callback
103+
expect(callbacks).to.have.length(0)
104+
cy
105+
.get('.control.callback .v-icon')
106+
// clicking the icon should fire the callback
107+
.click({ force: true })
108+
.then(() => {
109+
expect(callbacks).to.have.length(1)
110+
})
111+
112+
// TODO: visual regression test
113+
// https://github.com/cylc/cylc-ui/issues/178
114+
})
115+
116+
it('enables and disables if', () => {
117+
// mount the toolbar with a couple of controls
118+
mountToolbar([
119+
{
120+
title: 'Group 1',
121+
controls: [
122+
{
123+
title: 'Foo',
124+
icon: mdiCog,
125+
action: 'toggle',
126+
value: true,
127+
key: 'foo',
128+
enableIf: ['bar'],
129+
disableIf: ['baz']
130+
},
131+
{
132+
title: 'Bar',
133+
icon: mdiCog,
134+
action: 'toggle',
135+
value: true,
136+
key: 'bar'
137+
},
138+
{
139+
title: 'Baz',
140+
icon: mdiCog,
141+
action: 'toggle',
142+
value: false,
143+
key: 'baz'
144+
}
145+
]
146+
}
147+
])
148+
149+
cy
150+
// foo should start enabled (bar=true, baz=false)
151+
.get('.control.foo .v-icon')
152+
.not('.v-icon--disabled')
153+
154+
// toggle bar
155+
.get('.control.bar .v-icon')
156+
.click({ force: true })
157+
158+
// foo should be disabled (bar=false, baz=false)
159+
.get('.control.foo .v-icon')
160+
.should('have.class', 'v-icon--disabled')
161+
162+
// toggle bar & baz
163+
.get('.control.bar .v-icon')
164+
.click({ force: true })
165+
.get('.control.baz .v-icon')
166+
.click({ force: true })
167+
168+
// foo should be disabled (bar=true, baz=true)
169+
.get('.control.foo .v-icon')
170+
.should('have.class', 'v-icon--disabled')
171+
172+
// toggle baz
173+
.get('.control.baz .v-icon')
174+
.click({ force: true })
175+
176+
// foo should be enabled (bar=true, baz=false)
177+
.get('.control.foo .v-icon')
178+
.not('.v-icon--disabled')
179+
})
180+
181+
it('groups', () => {
182+
mountToolbar([
183+
{
184+
title: 'Group 1',
185+
controls: [
186+
{
187+
title: 'Foo',
188+
icon: mdiCog,
189+
action: 'toggle',
190+
value: true,
191+
key: 'foo'
192+
},
193+
{
194+
title: 'Bar',
195+
icon: mdiCog,
196+
action: 'toggle',
197+
value: false,
198+
key: 'bar'
199+
}
200+
]
201+
},
202+
{
203+
title: 'Group 2',
204+
controls: [
205+
{
206+
title: 'Baz',
207+
icon: mdiCog,
208+
action: 'toggle',
209+
value: true,
210+
key: 'baz'
211+
},
212+
{
213+
title: 'Pub',
214+
icon: mdiCog,
215+
action: 'toggle',
216+
value: false,
217+
key: 'pub'
218+
}
219+
]
220+
}
221+
])
222+
// TODO: visual regression test
223+
// https://github.com/cylc/cylc-ui/issues/178
224+
})
225+
})

cypress/support/component.js

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,46 @@ import '@/styles/index.scss'
2222
// Alternatively you can use CommonJS syntax:
2323
// require('./commands')
2424

25+
import Vue from 'vue'
26+
import Vuetify from 'vuetify'
27+
2528
import { mount } from 'cypress/vue2'
2629

30+
// vanilla mount function
31+
// e.g. cy.mount(MyComponent)
2732
Cypress.Commands.add('mount', mount)
2833

29-
// Example use:
30-
// cy.mount(MyComponent)
34+
// mount function with Vuetify installed
35+
// e.g. cy.mount(MyVuetifyComponent)
36+
// see also addVuetifyStyles
37+
Vue.use(Vuetify)
38+
Cypress.Commands.add('vmount', (component, options = {}) => {
39+
const vuetify = new Vuetify()
40+
return mount(
41+
component,
42+
{
43+
vuetify,
44+
...options
45+
}
46+
)
47+
})
48+
49+
// add required classes to the Cypress root element
50+
// e.g. cy.addVuetifyStyles(cy)
51+
// use this if you need Vuetify styles to be applied
52+
Cypress.Commands.add('addVuetifyStyles', (cy) => {
53+
cy
54+
.document()
55+
.then((foo) => {
56+
const classList = foo.children[0].classList
57+
if (!classList.contains('v-application')) {
58+
foo.children[0].classList.add(
59+
'v-application',
60+
'v-application--is-ltr',
61+
'theme--light',
62+
'job_theme--default'
63+
)
64+
}
65+
})
66+
})
67+

src/components/cylc/ViewToolbar.vue

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,45 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
5252

5353
<script>
5454
export default {
55+
name: 'ViewToolbar',
5556
props: {
5657
groups: {
5758
required: true,
5859
type: Array
60+
/*
61+
groups: [
62+
{
63+
// display name
64+
title: String,
65+
// list of controls in this group
66+
controls: [
67+
{
68+
// display name
69+
title: String,
70+
// unique key:
71+
// * Provided with "setOption" events.
72+
// * Used by enableIf/disableIf
73+
// * Added to the control's class list for testing.
74+
key: String
75+
// action to perform when clicked:
76+
// * toggle - toggle true/false
77+
// * callback - call the provided callback
78+
action: String
79+
// for use with action='callback'
80+
callback: Fuction
81+
// list of keys
82+
// only enable this control if all of the listed controls have
83+
// truthy values
84+
enableif
85+
// list of keys
86+
// disable this control if any of the listed controls have
87+
// truthy values
88+
disableIf
89+
}
90+
]
91+
}
92+
]
93+
*/
5994
}
6095
},
6196
computed: {
@@ -120,7 +155,7 @@ export default {
120155
},
121156
methods: {
122157
toggle (control, e) {
123-
// toogle a boolean value
158+
// toggle a boolean value
124159
// NOTE: undefined is interpreted is false
125160
control.value = !control.value
126161
this.$emit('setOption', control.key, control.value)
@@ -149,14 +184,31 @@ export default {
149184

150185
<style lang="scss">
151186
.c-view-toolbar {
187+
// give the toolbar a little respect space
152188
padding: 0.5rem 0 0.5rem 0;
153189
154190
.group {
191+
// put a bit of space between the groups
155192
padding-right: 0.5rem;
156193
display: inline-block;
157194
195+
&:before {
196+
// place a divider between groups
197+
content: '|';
198+
font-size: 2rem;
199+
position: relative;
200+
top: 0.5rem; // because the font is x2 nudge it down 1/2
201+
color: rgb(200, 200, 200);
202+
}
203+
&:first-child:before {
204+
// don't add a divider on the first group
205+
content: '';
206+
}
207+
158208
.control {
209+
// put a bit of space between the controls
159210
padding: 0 0 0 0.5rem;
211+
// make them sit side-by-side
160212
display: inline-block;
161213
}
162214
}

0 commit comments

Comments
 (0)