@rancher/shell 0.3.0 → 0.3.2
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.
- package/assets/styles/global/_button.scss +5 -1
- package/assets/styles/global/_columns.scss +4 -0
- package/assets/styles/global/_gauges.scss +1 -1
- package/assets/styles/global/_layout.scss +5 -2
- package/assets/styles/global/_select.scss +1 -4
- package/assets/styles/themes/_dark.scss +5 -4
- package/assets/styles/themes/_light.scss +4 -3
- package/assets/styles/themes/_suse.scss +1 -1
- package/assets/styles/vendor/vue-select.scss +4 -3
- package/assets/translations/en-us.yaml +673 -73
- package/assets/translations/zh-hans.yaml +720 -207
- package/chart/monitoring/steps/uninstall-v1.vue +2 -2
- package/cloud-credential/azure.vue +23 -0
- package/cloud-credential/harvester.vue +25 -62
- package/cloud-credential/pnap.vue +80 -0
- package/components/.DS_Store +0 -0
- package/components/ActionMenu.vue +28 -7
- package/components/AdvancedSection.vue +9 -2
- package/components/Alert.vue +2 -2
- package/components/ButtonDropdown.vue +0 -2
- package/components/ButtonGroup.vue +1 -0
- package/components/CollapsibleCard.vue +0 -1
- package/components/CruResource.vue +41 -4
- package/components/DetailTop.vue +72 -4
- package/components/DisableAuthProviderModal.vue +106 -0
- package/{rancher-components/components/Utils/DraggableZone → components}/DraggableZone.vue +0 -0
- package/components/ExplorerMembers.vue +253 -30
- package/components/ExplorerProjectsNamespaces.vue +77 -33
- package/components/ExtensionPanel.vue +42 -0
- package/components/GrowlManager.vue +3 -3
- package/components/IconOrSvg.vue +178 -0
- package/components/LogItem.vue +69 -0
- package/components/PodSecurityAdmission.vue +302 -0
- package/components/PromptModal.vue +1 -0
- package/components/ResourceDetail/Masthead.vue +69 -4
- package/components/ResourceDetail/index.vue +12 -5
- package/components/ResourceList/Masthead.vue +11 -1
- package/components/ResourceList/ResourceLoadingIndicator.vue +12 -2
- package/components/ResourceList/index.vue +66 -12
- package/components/ResourceList/resource-list.config.js +7 -0
- package/components/ResourceTable.vue +33 -6
- package/components/SimpleBox.vue +1 -1
- package/components/SortableTable/THead.vue +21 -14
- package/components/SortableTable/filtering.js +1 -1
- package/components/SortableTable/index.vue +21 -10
- package/components/SortableTable/selection.js +15 -3
- package/components/Tabbed/Tab.vue +1 -1
- package/components/Tabbed/index.vue +20 -15
- package/components/__tests__/.DS_Store +0 -0
- package/components/__tests__/AsyncButton.test.ts +140 -0
- package/components/__tests__/BackLink.test.ts +33 -0
- package/components/__tests__/ButtonGroup.test.ts +124 -0
- package/components/__tests__/ClusterBadge.test.ts +32 -0
- package/components/__tests__/CollapsibleCard.test.ts +64 -0
- package/components/__tests__/ConsumptionGauge.test.ts +88 -0
- package/components/__tests__/CruResource.test.ts +3 -2
- package/components/__tests__/FixedBanner.test.ts +129 -0
- package/components/__tests__/GrowlManager.test.ts +147 -0
- package/components/__tests__/NamespaceFilter.test.ts +33 -25
- package/components/__tests__/PercentageBar.test.ts +32 -0
- package/components/__tests__/PodSecurityAdmission.test.ts +398 -0
- package/components/auth/AuthBanner.vue +20 -10
- package/components/auth/RoleDetailEdit.vue +26 -17
- package/components/auth/SelectPrincipal.vue +36 -5
- package/components/form/ArrayList.vue +3 -35
- package/components/form/ArrayListGrouped.vue +13 -4
- package/components/form/ArrayListSelect.vue +5 -5
- package/components/form/Error.vue +8 -0
- package/components/form/KeyValue.vue +39 -7
- package/components/form/LabeledSelect.vue +5 -2
- package/components/form/Labels.vue +46 -16
- package/components/form/Members/ClusterPermissionsEditor.vue +17 -17
- package/components/form/Members/MembershipEditor.vue +12 -12
- package/components/form/NameNsDescription.vue +1 -1
- package/components/form/NodeScheduling.vue +1 -1
- package/components/form/Probe.vue +3 -3
- package/components/form/ResourceQuota/Project.vue +6 -6
- package/components/form/ResourceTabs/index.vue +24 -6
- package/components/form/Security.vue +7 -6
- package/components/form/Select.vue +3 -2
- package/components/form/SelectOrCreateAuthSecret.vue +22 -29
- package/components/form/ServicePorts.vue +8 -0
- package/components/form/WorkloadPorts.vue +7 -1
- package/components/form/__tests__/ArrayList.test.ts +74 -0
- package/components/form/__tests__/ArrayListGrouped.test.ts +6 -4
- package/components/formatter/Checked.vue +1 -1
- package/components/formatter/ClusterLink.vue +5 -0
- package/components/formatter/IconIsDefault.vue +2 -2
- package/components/formatter/InternalExternalIP.vue +11 -8
- package/components/formatter/LiveDuration.vue +78 -0
- package/components/formatter/WorkloadHealthScale.vue +5 -3
- package/components/nav/Header.vue +74 -7
- package/components/nav/NamespaceFilter.vue +146 -63
- package/components/nav/TopLevelMenu.vue +22 -19
- package/components/nav/WindowManager/ContainerLogs.vue +83 -126
- package/components/nav/WindowManager/ContainerShell.vue +9 -7
- package/components/nav/WindowManager/Window.vue +2 -0
- package/components/nav/WindowManager/index.vue +10 -0
- package/config/elemental-types.js +9 -0
- package/config/features.js +2 -0
- package/config/home-links.js +4 -1
- package/config/pod-security-admission.ts +82 -0
- package/config/product/apps.js +1 -1
- package/config/product/auth.js +6 -5
- package/config/product/backup.js +1 -1
- package/config/product/explorer.js +6 -6
- package/config/product/fleet.js +1 -1
- package/config/product/manager.js +6 -2
- package/config/query-params.js +1 -0
- package/config/secret.js +0 -1
- package/config/settings.ts +26 -9
- package/config/table-headers.js +22 -11
- package/config/types.js +4 -1
- package/config/uiplugins.js +3 -3
- package/content/docs/zh-hans/getting-started.md +113 -137
- package/content/docs/zh-hans/whats-new.md +8 -46
- package/core/plugin-helpers.js +171 -0
- package/core/plugin.ts +61 -1
- package/core/plugins.js +33 -0
- package/core/types.ts +128 -2
- package/creators/pkg/package-lock.json +37 -0
- package/creators/pkg/package.json +1 -1
- package/detail/catalog.cattle.io.app.vue +1 -1
- package/detail/pod.vue +1 -1
- package/detail/provisioning.cattle.io.cluster.vue +35 -9
- package/detail/service.vue +2 -9
- package/detail/workload/index.vue +0 -1
- package/dialog/AddClusterMemberDialog.vue +22 -28
- package/dialog/AddProjectMemberDialog.vue +53 -9
- package/dialog/DiagnosticTimingsDialog.vue +8 -7
- package/dialog/DrainNode.vue +44 -48
- package/dialog/ForceMachineRemoveDialog.vue +5 -7
- package/dialog/GenericPrompt.vue +15 -20
- package/dialog/RollbackWorkloadDialog.vue +15 -46
- package/dialog/RotateCertificatesDialog.vue +5 -7
- package/dialog/RotateEncryptionKeyDialog.vue +5 -9
- package/dialog/SaveAsRKETemplateDialog.vue +5 -13
- package/dialog/ScaleMachineDownDialog.vue +1 -1
- package/dialog/ScalePoolDownDialog.vue +121 -0
- package/edit/__tests__/management.cattle.io.setting.test.ts +3 -3
- package/edit/auth/azuread.vue +16 -16
- package/edit/auth/github.vue +8 -0
- package/edit/auth/googleoauth.vue +10 -1
- package/edit/auth/ldap/index.vue +10 -0
- package/edit/auth/oidc.vue +10 -0
- package/edit/auth/saml.vue +10 -0
- package/edit/autoscaling.horizontalpodautoscaler/index.vue +1 -1
- package/edit/catalog.cattle.io.clusterrepo.vue +3 -0
- package/edit/cloudcredential.vue +3 -7
- package/edit/logging-flow/Match.vue +39 -8
- package/edit/logging-flow/index.vue +27 -4
- package/edit/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +107 -0
- package/edit/management.cattle.io.project.vue +8 -1
- package/edit/management.cattle.io.setting.vue +5 -2
- package/edit/management.cattle.io.user.vue +7 -1
- package/edit/monitoring.coreos.com.alertmanagerconfig/receiverConfig.vue +36 -8
- package/edit/monitoring.coreos.com.alertmanagerconfig/types/email.vue +2 -2
- package/edit/monitoring.coreos.com.prometheusrule/GroupRules.vue +14 -6
- package/edit/namespace.vue +18 -4
- package/edit/networking.k8s.io.ingress/Certificate.vue +1 -0
- package/edit/networking.k8s.io.ingress/IngressClass.vue +8 -6
- package/edit/networking.k8s.io.ingress/RulePath.vue +12 -6
- package/edit/networking.k8s.io.ingress/index.vue +8 -6
- package/edit/persistentvolume/index.vue +30 -27
- package/edit/persistentvolume/plugins/cephfs.vue +29 -29
- package/edit/persistentvolume/plugins/csi.vue +102 -62
- package/edit/persistentvolume/plugins/fc.vue +19 -19
- package/edit/persistentvolume/plugins/iscsi.vue +45 -45
- package/edit/persistentvolume/plugins/rbd.vue +39 -39
- package/edit/persistentvolumeclaim.vue +78 -75
- package/edit/provisioning.cattle.io.cluster/MachinePool.vue +11 -7
- package/edit/provisioning.cattle.io.cluster/RegistryConfigs.vue +10 -1
- package/edit/provisioning.cattle.io.cluster/RegistryMirrors.vue +87 -27
- package/edit/provisioning.cattle.io.cluster/SelectCredential.vue +3 -6
- package/edit/provisioning.cattle.io.cluster/__tests__/rke2.test.ts +96 -0
- package/edit/provisioning.cattle.io.cluster/import.vue +1 -1
- package/edit/provisioning.cattle.io.cluster/index.vue +29 -6
- package/edit/provisioning.cattle.io.cluster/rke2.vue +445 -154
- package/edit/secret/index.vue +3 -7
- package/edit/service.vue +3 -1
- package/edit/storage.k8s.io.storageclass/index.vue +100 -16
- package/edit/storage.k8s.io.storageclass/provisioners/driver.harvesterhci.io.vue +114 -0
- package/edit/workload/__tests__/index.test.ts +98 -0
- package/edit/workload/index.vue +58 -8
- package/edit/workload/mixins/workload.js +107 -70
- package/edit/workload/storage/ContainerMountPaths.vue +0 -10
- package/edit/workload/storage/emptyDir.vue +88 -0
- package/edit/workload/storage/ephemeralVolume/index.vue +1 -1
- package/edit/workload/storage/index.vue +8 -0
- package/edit/workload/storage/persistentVolumeClaim/index.vue +1 -1
- package/layouts/default.vue +57 -44
- package/list/__tests__/workload.test.ts +5 -2
- package/list/catalog.cattle.io.app.vue +1 -0
- package/list/cis.cattle.io.clusterscan.vue +1 -0
- package/list/fleet.cattle.io.bundle.vue +5 -6
- package/list/fleet.cattle.io.cluster.vue +6 -3
- package/list/fleet.cattle.io.clusterregistrationtoken.vue +5 -6
- package/list/fleet.cattle.io.gitrepo.vue +4 -9
- package/list/helm.cattle.io.projecthelmchart.vue +1 -5
- package/list/logging.banzaicloud.io.clusterflow.vue +4 -1
- package/list/logging.banzaicloud.io.flow.vue +6 -5
- package/list/management.cattle.io.cluster.vue +1 -0
- package/list/management.cattle.io.feature.vue +3 -4
- package/list/management.cattle.io.podsecurityadmissionconfigurationtemplate.vue +47 -0
- package/list/management.cattle.io.setting.vue +2 -2
- package/list/management.cattle.io.user.vue +4 -10
- package/list/monitoring.coreos.com.alertmanagerconfig.vue +2 -7
- package/list/node.vue +8 -5
- package/list/persistentvolume.vue +3 -3
- package/list/persistentvolumeclaim.vue +3 -4
- package/list/provisioning.cattle.io.cluster.vue +18 -19
- package/list/service.vue +6 -14
- package/list/workload.vue +43 -38
- package/machine-config/azure.vue +429 -60
- package/machine-config/pnap.vue +288 -0
- package/mixins/auth-config.js +1 -3
- package/mixins/browser-tab-visibility.js +8 -14
- package/mixins/chart.js +1 -1
- package/mixins/create-edit-view/impl.js +4 -0
- package/mixins/create-edit-view/index.js +4 -2
- package/mixins/resource-fetch-namespaced.js +98 -0
- package/mixins/resource-fetch.js +79 -45
- package/mixins/resource-manager.js +1 -23
- package/models/apps.controllerrevision.js +7 -0
- package/models/apps.daemonset.js +18 -0
- package/models/apps.deployment.js +44 -0
- package/models/apps.replicaset.js +7 -0
- package/models/apps.statefulset.js +18 -0
- package/models/batch.job.js +7 -14
- package/models/cluster/node.js +10 -2
- package/models/cluster.x-k8s.io.machine.js +26 -4
- package/models/cluster.x-k8s.io.machinedeployment.js +12 -2
- package/models/event.js +7 -0
- package/models/logging.banzaicloud.io.flow.js +4 -0
- package/models/management.cattle.io.cluster.js +1 -1
- package/models/management.cattle.io.clusterroletemplatebinding.js +1 -1
- package/models/management.cattle.io.globalrole.js +2 -2
- package/models/management.cattle.io.node.js +37 -2
- package/models/management.cattle.io.podsecurityadmissionconfigurationtemplate.ts +4 -0
- package/models/management.cattle.io.project.js +30 -11
- package/models/management.cattle.io.setting.js +1 -1
- package/models/management.cattle.io.user.js +37 -1
- package/models/namespace.js +42 -5
- package/models/persistentvolume.js +14 -2
- package/models/pod.js +15 -0
- package/models/projectroletemplatebinding.js +7 -0
- package/models/provisioning.cattle.io.cluster.js +61 -10
- package/models/rke-machine.cattle.io.pnapmachinetemplate.js +15 -0
- package/models/service.js +14 -13
- package/models/storage.k8s.io.storageclass.js +33 -18
- package/models/workload.js +38 -7
- package/nuxt.config.js +27 -17
- package/package.json +7 -7
- package/pages/about.vue +14 -2
- package/pages/c/_cluster/apps/charts/index.vue +21 -3
- package/pages/c/_cluster/apps/charts/install.vue +59 -22
- package/pages/c/_cluster/auth/config/_id.vue +6 -0
- package/pages/c/_cluster/auth/config/index.vue +8 -6
- package/pages/c/_cluster/auth/group.principal/assign-edit.vue +1 -1
- package/pages/c/_cluster/auth/roles/index.vue +1 -1
- package/pages/c/_cluster/explorer/index.vue +51 -6
- package/pages/c/_cluster/longhorn/index.vue +1 -1
- package/pages/c/_cluster/monitoring/alertmanagerconfig/_alertmanagerconfigid/receiver.vue +15 -4
- package/pages/c/_cluster/monitoring/index.vue +1 -1
- package/pages/c/_cluster/neuvector/index.vue +1 -1
- package/pages/c/_cluster/settings/performance.vue +48 -2
- package/pages/c/_cluster/uiplugins/DeveloperInstallDialog.vue +2 -0
- package/pages/c/_cluster/uiplugins/InstallDialog.vue +3 -0
- package/pages/c/_cluster/uiplugins/PluginInfoPanel.vue +42 -2
- package/pages/c/_cluster/uiplugins/RemoveUIPlugins.vue +2 -0
- package/pages/c/_cluster/uiplugins/SetupUIPlugins.vue +1 -0
- package/pages/c/_cluster/uiplugins/UninstallDialog.vue +2 -0
- package/pages/c/_cluster/uiplugins/index.vue +42 -3
- package/pages/diagnostic.vue +5 -4
- package/pages/home.vue +105 -30
- package/pages/prefs.vue +23 -12
- package/pages/rio/mesh.vue +1 -1
- package/pkg/dynamic-importer.lib.js +8 -0
- package/pkg/vue.config.js +4 -0
- package/plugins/dashboard-store/__tests__/mutations.spec.js +406 -0
- package/plugins/dashboard-store/actions.js +32 -25
- package/plugins/dashboard-store/getters.js +50 -33
- package/plugins/dashboard-store/mutations.js +134 -28
- package/plugins/dashboard-store/resource-class.js +37 -42
- package/plugins/steve/actions.js +30 -0
- package/plugins/steve/caches/resourceCache.js +60 -0
- package/plugins/steve/getters.js +44 -1
- package/plugins/steve/mutations.js +97 -36
- package/plugins/steve/resourceWatcher.js +277 -0
- package/plugins/steve/schema.utils.js +25 -0
- package/plugins/steve/subscribe.js +288 -115
- package/plugins/steve/worker/index.js +17 -0
- package/plugins/steve/worker/web-worker.advanced.js +302 -0
- package/plugins/steve/{web-worker.steve-sub-worker.js → worker/web-worker.basic.js} +3 -44
- package/rancher-components/Card/Card.vue +3 -3
- package/rancher-components/Form/TextArea/TextAreaAutoGrow.vue +1 -0
- package/rancher-components/StringList/StringList.test.ts +45 -420
- package/rancher-components/StringList/StringList.vue +1 -10
- package/rancher-components/components/Banner/Banner.test.ts +44 -0
- package/rancher-components/components/Banner/Banner.vue +130 -61
- package/rancher-components/components/Form/Checkbox/Checkbox.test.ts +13 -22
- package/rancher-components/components/Form/Checkbox/Checkbox.vue +8 -6
- package/rancher-components/components/Form/ToggleSwitch/ToggleSwitch.test.ts +9 -9
- package/rancher-components/components/LabeledTooltip/LabeledTooltip.vue +0 -1
- package/rancher-components/components/StringList/StringList.test.ts +7 -7
- package/rancher-components/components/StringList/StringList.vue +21 -15
- package/scripts/test-plugins-build.sh +8 -0
- package/static/loading-indicator.html +1 -1
- package/store/action-menu.js +4 -3
- package/store/index.js +54 -3
- package/store/plugins.js +0 -17
- package/store/pnap.js +128 -0
- package/store/prefs.js +4 -2
- package/store/type-map.js +81 -13
- package/types/pod-security-admission.ts +36 -0
- package/types/shell/index.d.ts +497 -396
- package/utils/__tests__/object.test.ts +17 -1
- package/utils/__tests__/pod-security-admission.test.ts +61 -0
- package/utils/async.ts +36 -0
- package/utils/color.js +45 -0
- package/utils/crypto/browserHashUtils.js +18 -0
- package/utils/dynamic-importer.js +8 -0
- package/utils/install-redirect.js +1 -1
- package/utils/object.js +24 -0
- package/utils/pod-security-admission.ts +39 -0
- package/utils/socket.js +61 -24
- package/utils/string.js +2 -0
- package/utils/svg-filter.js +301 -0
- package/utils/time.js +49 -0
- package/utils/validators/cidr.js +4 -0
- package/utils/validators/formRules/__tests__/index.test.ts +23 -3
- package/utils/validators/formRules/index.ts +14 -0
- package/config/product/harvester-manager.js +0 -162
- package/edit/harvesterhci.io.management.cluster.vue +0 -153
- package/list/harvesterhci.io.management.cluster.vue +0 -241
- package/machine-config/harvester.vue +0 -693
- package/models/harvesterhci.io.management.cluster.js +0 -228
- package/pages/c/_cluster/harvesterManager/index.vue +0 -24
- package/rancher-components/Card/Card.test.ts +0 -39
- package/rancher-components/Utils/DraggableZone/DraggableZone.vue +0 -181
- package/rancher-components/Utils/DraggableZone/index.ts +0 -1
- package/rancher-components/components/Utils/DraggableZone/index.ts +0 -1
|
@@ -1,455 +1,80 @@
|
|
|
1
|
-
import { mount
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
2
|
import { StringList } from './index';
|
|
3
3
|
|
|
4
4
|
describe('StringList.vue', () => {
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
beforeEach(() => {
|
|
9
|
-
wrapper = mount(StringList, {
|
|
6
|
+
it('is empty', () => {
|
|
7
|
+
const wrapper = mount(StringList, {
|
|
10
8
|
propsData: { items: [] },
|
|
11
9
|
});
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
describe('List box', () => {
|
|
15
|
-
|
|
16
|
-
it('is empty', () => {
|
|
17
|
-
const box = wrapper.find('[data-testid="div-string-list-box"]').element as HTMLElement;
|
|
18
|
-
|
|
19
|
-
expect(box.children.length).toBe(0);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it('show multiple items', async () => {
|
|
23
|
-
const items = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k' ];
|
|
24
|
-
await wrapper.setProps({ items });
|
|
25
|
-
|
|
26
|
-
const elements = wrapper.findAll('[data-testid^="div-item"]');
|
|
27
|
-
|
|
28
|
-
expect(elements.length).toBe(10);
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
it('double click triggers inline edit mode', async () => {
|
|
32
|
-
const items = [ 'test' ];
|
|
33
|
-
await wrapper.setProps({ items });
|
|
34
|
-
|
|
35
|
-
const item = wrapper.find('[data-testid="div-item-test"]');
|
|
36
|
-
await item.trigger('dblclick');
|
|
37
|
-
|
|
38
|
-
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
|
39
|
-
|
|
40
|
-
expect(inputField.element).toBeDefined();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('double click on empty space triggers create mode', async () => {
|
|
44
|
-
await wrapper.setProps({ items: [] });
|
|
45
|
-
|
|
46
|
-
// double click on empty space
|
|
47
|
-
const box = wrapper.find('[data-testid="div-string-list-box"]');
|
|
48
|
-
await box.trigger('dblclick');
|
|
49
|
-
|
|
50
|
-
const inputField = wrapper.find('[data-testid="item-create"]');
|
|
51
|
-
|
|
52
|
-
expect(inputField.element).toBeDefined();
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('select item when click on it', async () => {
|
|
56
|
-
const items = [ 'test' ];
|
|
57
|
-
await wrapper.setProps({ items });
|
|
58
|
-
|
|
59
|
-
// select item
|
|
60
|
-
const item = wrapper.find('[data-testid^="div-item"]');
|
|
61
|
-
await item.trigger('mousedown');
|
|
62
|
-
|
|
63
|
-
expect(item.element.className).toContain('selected');
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('double click to edit item not allowed when readonly', async () => {
|
|
67
|
-
const items = [ 'test' ];
|
|
68
|
-
await wrapper.setProps({
|
|
69
|
-
items,
|
|
70
|
-
readonly: true,
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
const item = wrapper.find('[data-testid="div-item-test"]');
|
|
74
|
-
await item.trigger('dblclick');
|
|
75
|
-
|
|
76
|
-
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
|
77
|
-
|
|
78
|
-
expect(inputField.element).toBeUndefined();
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('double click on empty space to create item not allowed when readonly', async () => {
|
|
82
|
-
await wrapper.setProps({
|
|
83
|
-
items: [],
|
|
84
|
-
readonly: true,
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
// double click on empty space
|
|
88
|
-
const box = wrapper.find('[data-testid="div-string-list-box"]');
|
|
89
|
-
await box.trigger('dblclick');
|
|
90
|
-
|
|
91
|
-
const inputField = wrapper.find('[data-testid="item-create"]');
|
|
92
|
-
|
|
93
|
-
expect(inputField.element).toBeUndefined();
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('select item not allowed when readonly', async () => {
|
|
97
|
-
const items = [ 'test' ];
|
|
98
|
-
await wrapper.setProps({
|
|
99
|
-
items,
|
|
100
|
-
readonly: true,
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
// select item
|
|
104
|
-
const item = wrapper.find('[data-testid^="div-item"]');
|
|
105
|
-
await item.trigger('mousedown');
|
|
106
|
-
|
|
107
|
-
expect(item.element.className).not.toContain('selected');
|
|
108
|
-
});
|
|
10
|
+
const box = wrapper.find('[data-testid="div-string-list-box"]').element as HTMLElement;
|
|
109
11
|
|
|
12
|
+
expect(box.children.length).toBe(0);
|
|
110
13
|
});
|
|
111
14
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const actionButtons = wrapper.find('[data-testid="div-action-buttons"]');
|
|
116
|
-
|
|
117
|
-
expect(actionButtons.element).toBeDefined();
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it('are hidden when is view-only mode', async () => {
|
|
121
|
-
await wrapper.setProps({
|
|
122
|
-
readonly: true,
|
|
123
|
-
});
|
|
124
|
-
const actionButtons = wrapper.find('[data-testid="div-action-buttons"]');
|
|
125
|
-
|
|
126
|
-
expect(actionButtons.element).toBeUndefined();
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
describe('Add button', () => {
|
|
130
|
-
|
|
131
|
-
it('is enabled by default', async () => {
|
|
132
|
-
const addButton = wrapper.find('[data-testid="button-add"]')?.element as HTMLButtonElement;
|
|
133
|
-
|
|
134
|
-
expect(addButton.disabled).toBe(false);
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
it('show the input field when is clicked', async () => {
|
|
138
|
-
// click add button
|
|
139
|
-
const addButton = wrapper.find('[data-testid="button-add"]');
|
|
140
|
-
await addButton.trigger('click');
|
|
141
|
-
|
|
142
|
-
const inputField = wrapper.find('[data-testid="item-create"]');
|
|
143
|
-
|
|
144
|
-
expect(inputField.element).toBeDefined();
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('is disabled when create mode is active', async () => {
|
|
148
|
-
// click add button
|
|
149
|
-
const addButton = wrapper.find('[data-testid="button-add"]');
|
|
150
|
-
await addButton.trigger('click');
|
|
151
|
-
|
|
152
|
-
wrapper.find('[data-testid="item-create"]');
|
|
153
|
-
|
|
154
|
-
const buttonElem = addButton.element as HTMLButtonElement;
|
|
155
|
-
|
|
156
|
-
expect(buttonElem.disabled).toBe(true);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
describe('Remove button', () => {
|
|
162
|
-
|
|
163
|
-
it('is disabled by default', async () => {
|
|
164
|
-
const removeButton = wrapper.find('[data-testid="button-remove"]');
|
|
165
|
-
const buttonElem = removeButton.element as HTMLButtonElement;
|
|
166
|
-
|
|
167
|
-
expect(buttonElem.disabled).toBe(true);
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
it('is enabled when create mode is active', async () => {
|
|
171
|
-
// click add button
|
|
172
|
-
const addButton = wrapper.find('[data-testid="button-add"]');
|
|
173
|
-
await addButton.trigger('click');
|
|
174
|
-
|
|
175
|
-
const removeButton = wrapper.find('[data-testid="button-remove"]');
|
|
176
|
-
const buttonElem = removeButton.element as HTMLButtonElement;
|
|
177
|
-
|
|
178
|
-
expect(buttonElem.disabled).toBe(false);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('is enabled when edit mode is active', async () => {
|
|
182
|
-
const items = [ 'test' ];
|
|
183
|
-
await wrapper.setProps({ items });
|
|
184
|
-
|
|
185
|
-
// activate edit mode
|
|
186
|
-
await wrapper.setData({ editedItem: 'test' });
|
|
187
|
-
|
|
188
|
-
const removeButton = wrapper.find('[data-testid="button-remove"]');
|
|
189
|
-
const buttonElem = removeButton.element as HTMLButtonElement;
|
|
190
|
-
|
|
191
|
-
expect(buttonElem.disabled).toBe(false);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
it('is enabled when an item is selected', async () => {
|
|
195
|
-
const items = [ 'test' ];
|
|
196
|
-
await wrapper.setProps({ items });
|
|
197
|
-
|
|
198
|
-
// select item
|
|
199
|
-
await wrapper.setData({ selected: 'test' });
|
|
200
|
-
|
|
201
|
-
// click remove button
|
|
202
|
-
const removeButton = wrapper.find('[data-testid="button-remove"]');
|
|
203
|
-
const buttonElem = removeButton.element as HTMLButtonElement;
|
|
204
|
-
|
|
205
|
-
expect(buttonElem.disabled).toBe(false);
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
it('removes items when an item is selected', async () => {
|
|
209
|
-
const items = [ 'a' ];
|
|
210
|
-
await wrapper.setProps({ items });
|
|
211
|
-
|
|
212
|
-
// select item
|
|
213
|
-
await wrapper.setData({ selected: 'a' });
|
|
214
|
-
|
|
215
|
-
// click remove button
|
|
216
|
-
const removeButton = wrapper.find('[data-testid="button-remove"]');
|
|
217
|
-
await removeButton.trigger('mousedown');
|
|
218
|
-
|
|
219
|
-
await wrapper.vm.$nextTick();
|
|
220
|
-
|
|
221
|
-
const itemsCount = (wrapper.emitted('change') as any)[0][0].length;
|
|
222
|
-
|
|
223
|
-
expect(itemsCount).toBe(0);
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
it('deactivates create mode', async () => {
|
|
227
|
-
// activate create mode
|
|
228
|
-
await wrapper.setData({ isCreateItem: true });
|
|
229
|
-
|
|
230
|
-
// click remove button
|
|
231
|
-
const removeButton = wrapper.find('[data-testid="button-remove"]');
|
|
232
|
-
await removeButton.trigger('mousedown');
|
|
233
|
-
|
|
234
|
-
const inputField = await wrapper.find('[data-testid="item-create"]');
|
|
235
|
-
|
|
236
|
-
expect(inputField.element).toBeUndefined();
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
it('deactivates edit mode', async () => {
|
|
240
|
-
const items = [ 'test' ];
|
|
241
|
-
await wrapper.setProps({ items });
|
|
242
|
-
|
|
243
|
-
// activate edit mode
|
|
244
|
-
await wrapper.setData({ editedItem: 'test' });
|
|
245
|
-
|
|
246
|
-
// click remove button
|
|
247
|
-
const removeButton = wrapper.find('[data-testid="button-remove"]');
|
|
248
|
-
await removeButton.trigger('mousedown');
|
|
249
|
-
|
|
250
|
-
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
|
251
|
-
|
|
252
|
-
expect(inputField.element).toBeUndefined();
|
|
253
|
-
});
|
|
254
|
-
|
|
15
|
+
it('is showing one element', () => {
|
|
16
|
+
const wrapper = mount(StringList, {
|
|
17
|
+
propsData: { items: ['test'] },
|
|
255
18
|
});
|
|
19
|
+
const box = wrapper.find('.string-list-box').element as HTMLElement;
|
|
256
20
|
|
|
21
|
+
expect(box.children.length).toBe(1);
|
|
257
22
|
});
|
|
258
23
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
const emptyItem = ' ';
|
|
263
|
-
|
|
264
|
-
it('save a new item in create mode by pressing Enter key', async () => {
|
|
265
|
-
// activate create mode
|
|
266
|
-
await wrapper.setData({ isCreateItem: true });
|
|
267
|
-
|
|
268
|
-
// type item name
|
|
269
|
-
const inputField = wrapper.find('[data-testid="item-create"]');
|
|
270
|
-
await inputField.setValue(validItem);
|
|
271
|
-
|
|
272
|
-
// press enter
|
|
273
|
-
await inputField.trigger('keydown.enter');
|
|
274
|
-
await wrapper.vm.$nextTick();
|
|
275
|
-
|
|
276
|
-
const emitted = (wrapper.emitted('change') as any)[0][0][0];
|
|
277
|
-
|
|
278
|
-
expect(emitted).toBe(validItem.trim());
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
it('save item in edit mode by pressing Enter key', async () => {
|
|
282
|
-
const items = [ 'test' ];
|
|
283
|
-
await wrapper.setProps({ items });
|
|
284
|
-
|
|
285
|
-
// activate edit mode
|
|
286
|
-
await wrapper.setData({ editedItem: 'test' });
|
|
287
|
-
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
|
288
|
-
|
|
289
|
-
// edit item name
|
|
290
|
-
await inputField.setValue(validItem);
|
|
291
|
-
|
|
292
|
-
// press enter
|
|
293
|
-
await inputField.trigger('keydown.enter');
|
|
294
|
-
await wrapper.vm.$nextTick();
|
|
295
|
-
|
|
296
|
-
const emitted = (wrapper.emitted('change') as any)[0][0][0];
|
|
297
|
-
|
|
298
|
-
expect(emitted).toBe(validItem.trim());
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
it('reject a new item in create mode when item name is empty', async () => {
|
|
302
|
-
// activate create mode
|
|
303
|
-
await wrapper.setData({ isCreateItem: true });
|
|
304
|
-
|
|
305
|
-
// type item name
|
|
306
|
-
const inputField = wrapper.find('[data-testid="item-create"]');
|
|
307
|
-
await inputField.setValue(emptyItem);
|
|
308
|
-
|
|
309
|
-
// press enter
|
|
310
|
-
await inputField.trigger('keydown.enter');
|
|
311
|
-
await wrapper.vm.$nextTick();
|
|
312
|
-
|
|
313
|
-
expect(wrapper.emitted('change')).toBeFalsy();
|
|
314
|
-
});
|
|
315
|
-
|
|
316
|
-
it('reject a new item in create mode when item name is duplicate', async () => {
|
|
317
|
-
const items = [ 'test' ];
|
|
318
|
-
await wrapper.setProps({ items });
|
|
319
|
-
|
|
320
|
-
// activate create mode
|
|
321
|
-
await wrapper.setData({ isCreateItem: true });
|
|
322
|
-
|
|
323
|
-
// type item name
|
|
324
|
-
const inputField = wrapper.find('[data-testid="item-create"]');
|
|
325
|
-
await inputField.setValue('test');
|
|
326
|
-
|
|
327
|
-
// press enter
|
|
328
|
-
await inputField.trigger('keydown.enter');
|
|
329
|
-
await wrapper.vm.$nextTick();
|
|
330
|
-
|
|
331
|
-
expect(wrapper.emitted('change')).toBeFalsy();
|
|
24
|
+
it('action-buttons are visible', () => {
|
|
25
|
+
const wrapper = mount(StringList, {
|
|
26
|
+
propsData: { items: ['test'] },
|
|
332
27
|
});
|
|
28
|
+
const actionButtons = wrapper.find('[data-testid="div-action-buttons"]').element as HTMLElement;
|
|
333
29
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
await wrapper.setProps({ items });
|
|
337
|
-
|
|
338
|
-
// activate edit mode
|
|
339
|
-
await wrapper.setData({ editedItem: 'test' });
|
|
340
|
-
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
|
341
|
-
|
|
342
|
-
// edit item name
|
|
343
|
-
await inputField.setValue(emptyItem);
|
|
344
|
-
|
|
345
|
-
// press enter
|
|
346
|
-
await inputField.trigger('keydown.enter');
|
|
347
|
-
await wrapper.vm.$nextTick();
|
|
348
|
-
|
|
349
|
-
expect(wrapper.emitted('change')).toBeFalsy();
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
it('reject an item in edit mode when item name is duplicate', async () => {
|
|
353
|
-
const items = [ 'test', 'test-1' ];
|
|
354
|
-
await wrapper.setProps({ items });
|
|
355
|
-
|
|
356
|
-
// activate edit mode
|
|
357
|
-
await wrapper.setData({ editedItem: 'test' });
|
|
358
|
-
const inputField = wrapper.find('[data-testid^="item-edit"]');
|
|
359
|
-
|
|
360
|
-
// edit item name
|
|
361
|
-
await inputField.setValue('test-1');
|
|
362
|
-
|
|
363
|
-
// press enter
|
|
364
|
-
await inputField.trigger('keydown.enter');
|
|
365
|
-
await wrapper.vm.$nextTick();
|
|
30
|
+
expect(actionButtons).not.toBe(undefined);
|
|
31
|
+
});
|
|
366
32
|
|
|
367
|
-
|
|
33
|
+
it('action-buttons are hidden when is view-only mode', () => {
|
|
34
|
+
const wrapper = mount(StringList, {
|
|
35
|
+
propsData: {
|
|
36
|
+
items: ['test'],
|
|
37
|
+
readonly: true,
|
|
38
|
+
},
|
|
368
39
|
});
|
|
40
|
+
const actionButtons = wrapper.find('[data-testid="div-action-buttons"]').element as HTMLElement;
|
|
369
41
|
|
|
42
|
+
expect(actionButtons).toBe(undefined);
|
|
370
43
|
});
|
|
371
44
|
|
|
372
|
-
|
|
45
|
+
it('show new item when "items" property change', async () => {
|
|
46
|
+
const items = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k'];
|
|
373
47
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
await wrapper.setProps({
|
|
48
|
+
const wrapper = mount(StringList, {
|
|
49
|
+
propsData: {
|
|
377
50
|
items,
|
|
378
|
-
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
// activate edit mode
|
|
382
|
-
await wrapper.setData({ isCreateItem: true });
|
|
383
|
-
|
|
384
|
-
// type item name
|
|
385
|
-
const inputField = wrapper.find('[data-testid="item-create"]');
|
|
386
|
-
await inputField.setValue('test');
|
|
387
|
-
|
|
388
|
-
const icon = wrapper.find('[data-testid="i-warning-icon"]');
|
|
389
|
-
|
|
390
|
-
expect(icon.element).toBeDefined();
|
|
51
|
+
},
|
|
391
52
|
});
|
|
53
|
+
const elements = wrapper.findAll('[data-testid^="div-item"]');
|
|
392
54
|
|
|
393
|
-
|
|
394
|
-
const items = [ 'test' ];
|
|
395
|
-
await wrapper.setProps({
|
|
396
|
-
items,
|
|
397
|
-
errorMessages: { duplicate: 'error, item is duplicate.' },
|
|
398
|
-
});
|
|
55
|
+
expect(elements.length).toBe(10);
|
|
399
56
|
|
|
400
|
-
|
|
401
|
-
await wrapper.setData({ isCreateItem: true });
|
|
57
|
+
await wrapper.setProps({ items: [ ...items, 'new' ] });
|
|
402
58
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
59
|
+
const newElements = wrapper.findAll('[data-testid^="div-item"]');
|
|
60
|
+
expect(newElements.length).toBe(11);
|
|
61
|
+
});
|
|
406
62
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
expect(message.element).toBeDefined();
|
|
410
|
-
});
|
|
63
|
+
it('remove item when "items" property change', async () => {
|
|
64
|
+
const items = [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k'];
|
|
411
65
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
await wrapper.setProps({
|
|
66
|
+
const wrapper = mount(StringList, {
|
|
67
|
+
propsData: {
|
|
415
68
|
items,
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// activate edit mode
|
|
419
|
-
await wrapper.setData({ isCreateItem: true });
|
|
420
|
-
|
|
421
|
-
// type item name
|
|
422
|
-
const inputField = wrapper.find('[data-testid="item-create"]');
|
|
423
|
-
await inputField.setValue('test');
|
|
424
|
-
|
|
425
|
-
const isDuplicate = (wrapper.emitted('errors') as any)[0][0].duplicate;
|
|
426
|
-
|
|
427
|
-
expect(isDuplicate).toBe(true);
|
|
69
|
+
},
|
|
428
70
|
});
|
|
71
|
+
const elements = wrapper.findAll('[data-testid^="div-item"]');
|
|
72
|
+
expect(elements.length).toBe(10);
|
|
429
73
|
|
|
430
|
-
|
|
431
|
-
const items = [ 'test' ];
|
|
432
|
-
await wrapper.setProps({
|
|
433
|
-
items,
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
// activate edit mode
|
|
437
|
-
await wrapper.setData({ isCreateItem: true });
|
|
438
|
-
|
|
439
|
-
// type item name
|
|
440
|
-
const inputField = wrapper.find('[data-testid="item-create"]');
|
|
441
|
-
|
|
442
|
-
// emit duplicate errors
|
|
443
|
-
await inputField.setValue('test');
|
|
444
|
-
|
|
445
|
-
// it is not duplicate, reset duplicate error -> emit false
|
|
446
|
-
await inputField.setValue('test-1');
|
|
447
|
-
|
|
448
|
-
const isDuplicate = (wrapper.emitted('errors') as any)[0][0].duplicate;
|
|
449
|
-
|
|
450
|
-
expect(isDuplicate).toBe(false);
|
|
451
|
-
});
|
|
74
|
+
await wrapper.setProps({ items: [ ...items.filter(f => f !== 'a') ] });
|
|
452
75
|
|
|
76
|
+
const newElements = wrapper.findAll('[data-testid^="div-item"]');
|
|
77
|
+
expect(newElements.length).toBe(9);
|
|
453
78
|
});
|
|
454
79
|
|
|
455
80
|
});
|
|
@@ -410,7 +410,6 @@ export default Vue.extend({
|
|
|
410
410
|
<LabeledInput
|
|
411
411
|
v-if="editedItem && editedItem === item"
|
|
412
412
|
ref="item-edit"
|
|
413
|
-
:data-testid="`item-edit-${item}`"
|
|
414
413
|
class="edit-input static"
|
|
415
414
|
:value="value != null ? value : item"
|
|
416
415
|
@input="onChange($event)"
|
|
@@ -424,7 +423,6 @@ export default Vue.extend({
|
|
|
424
423
|
>
|
|
425
424
|
<LabeledInput
|
|
426
425
|
ref="item-create"
|
|
427
|
-
data-testid="item-create"
|
|
428
426
|
class="create-input static"
|
|
429
427
|
type="text"
|
|
430
428
|
:value="value"
|
|
@@ -445,7 +443,6 @@ export default Vue.extend({
|
|
|
445
443
|
class="action-buttons"
|
|
446
444
|
>
|
|
447
445
|
<button
|
|
448
|
-
data-testid="button-remove"
|
|
449
446
|
class="btn btn-sm role-tertiary remove-button"
|
|
450
447
|
:disabled="!selected && !isCreateItem && !editedItem"
|
|
451
448
|
@mousedown.prevent="onClickMinusButton"
|
|
@@ -453,7 +450,6 @@ export default Vue.extend({
|
|
|
453
450
|
<span class="icon icon-minus icon-sm" />
|
|
454
451
|
</button>
|
|
455
452
|
<button
|
|
456
|
-
data-testid="button-add"
|
|
457
453
|
class="btn btn-sm role-tertiary add-button"
|
|
458
454
|
:disabled="isCreateItem || editedItem"
|
|
459
455
|
@click.prevent="onClickPlusButton"
|
|
@@ -462,15 +458,10 @@ export default Vue.extend({
|
|
|
462
458
|
</button>
|
|
463
459
|
</div>
|
|
464
460
|
<div class="messages">
|
|
465
|
-
<i
|
|
466
|
-
v-if="errorMessagesArray.length > 0"
|
|
467
|
-
data-testid="i-warning-icon"
|
|
468
|
-
class="icon icon-warning icon-lg"
|
|
469
|
-
/>
|
|
461
|
+
<i v-if="errorMessagesArray.length > 0" class="icon icon-warning icon-lg" />
|
|
470
462
|
<span
|
|
471
463
|
v-for="(msg, idx) in errorMessagesArray"
|
|
472
464
|
:key="idx"
|
|
473
|
-
:data-testid="`span-error-message-${msg}`"
|
|
474
465
|
class="error"
|
|
475
466
|
>
|
|
476
467
|
{{ idx > 0 ? '; ' : '' }}
|
|
@@ -10,4 +10,48 @@ describe('component: Banner', () => {
|
|
|
10
10
|
|
|
11
11
|
expect(element.textContent).toBe(label);
|
|
12
12
|
});
|
|
13
|
+
|
|
14
|
+
it('should display an icon', () => {
|
|
15
|
+
const icon = 'my-icon';
|
|
16
|
+
const wrapper = mount(Banner, { propsData: { icon } });
|
|
17
|
+
|
|
18
|
+
const element = wrapper.find(`.${ icon }`).element;
|
|
19
|
+
|
|
20
|
+
expect(element.classList).toContain(icon);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should not display an icon', () => {
|
|
24
|
+
const wrapper = mount(Banner);
|
|
25
|
+
|
|
26
|
+
const element = wrapper.find(`[data-testid="banner-icon"]`).element;
|
|
27
|
+
|
|
28
|
+
expect(element).not.toBeDefined();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('should emit close event', () => {
|
|
32
|
+
const wrapper = mount(Banner, { propsData: { closable: true } });
|
|
33
|
+
const element = wrapper.find(`[data-testid="banner-close"]`).element;
|
|
34
|
+
|
|
35
|
+
element.click();
|
|
36
|
+
|
|
37
|
+
expect(wrapper.emitted('close')).toHaveLength(1);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should add the right color', () => {
|
|
41
|
+
const color = 'red';
|
|
42
|
+
const wrapper = mount(Banner, { propsData: { color } });
|
|
43
|
+
|
|
44
|
+
const element = wrapper.element;
|
|
45
|
+
|
|
46
|
+
expect(element.classList).toContain(color);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should stack the banner messages', () => {
|
|
50
|
+
const stacked = true;
|
|
51
|
+
const wrapper = mount(Banner, { propsData: { stacked } });
|
|
52
|
+
|
|
53
|
+
const element = wrapper.find(`[data-testid="banner-content"]`).element;
|
|
54
|
+
|
|
55
|
+
expect(element.classList).toContain('stacked');
|
|
56
|
+
});
|
|
13
57
|
});
|