diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 0000000..3e3b098 --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,6 @@ +# Changelog + + +## 0.8.0 (2021-08-15) + +- Initial version diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..25f9359 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Ville Säävuori + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..3d4992b --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +# Vite Typescript + PrimeVue Starter + + +## Features + +- Vue 3.2 (with script setup) +- Routing using [vue-router 4](https://next.router.vuejs.org/) +- TypeScript 4.3 +- PostCSS 8 w/ `postcss-nesting` plugin +- Eslint +- Prettier +- Testing with cypress and Jest + +## IDE + +If you use IntelliJ you should use node-modules linler mode in .yarnrc.yml, +otherwise IntelliJ has problems with Autocompletion. + +## Project setup and usage + +Install dependencies: + +``` +yarn +``` + +Run development server: + +``` +yarn dev +``` + +Open Cypress test runner: + +``` +yarn test +``` + +Run Cypress tests in headless mode: + +``` +yarn test:ci +``` + +Build and preview built site locally: + +``` +yarn preview +``` + +Build: + +``` +yarn build +``` diff --git a/cypress.json b/cypress.json new file mode 100644 index 0000000..8a8c562 --- /dev/null +++ b/cypress.json @@ -0,0 +1,9 @@ +{ + "baseUrl": "http://localhost:3000", + "fixturesFolder": "tests/e2e/fixtures", + "integrationFolder": "tests/e2e/integration", + "pluginsFile": "tests/e2e/plugins/index.ts", + "screenshotsFolder": "tests/e2e/screenshots", + "supportFile": "tests/e2e/support/index.ts", + "videosFolder": "tests/e2e/videos" +} diff --git a/index.html b/index.html new file mode 100644 index 0000000..fd152ae --- /dev/null +++ b/index.html @@ -0,0 +1,14 @@ + + + + + + + + Vite Typescript + PrimeVue Starter + + +
+ + + diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..77054a8 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,16 @@ +// jest.config.js +module.exports = { + moduleFileExtensions: [ + 'js', + 'ts', + 'json', + 'vue' + ], + transform: { + '^.+\\.ts$': 'ts-jest', + '^.+\\.vue$': 'vue-jest' + }, + roots: [ + "./tests/unit" + ] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c189e36 --- /dev/null +++ b/package.json @@ -0,0 +1,74 @@ +{ + "name": "vite-primevue-starter", + "version": "0.8.0", + "license": "MIT", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite build && vite preview", + "start": "yarn dev & wait-on tcp:3000 -v", + "test:e2e": "yarn dev & wait-on tcp:3000 -v & cypress open", + "test:unit": "jest", + "test:ci": "yarn dev & wait-on tcp:3000 -v & cypress run --headless" + }, + "dependencies": { + "@vueuse/head": "~0.6", + "chart.js": "^3.5.0", + "pinia": "~2.0.0-rc.4", + "primeflex": "^3.0.1", + "primeicons": "^4.1.0", + "primevue": "^3.6.2", + "sass": "^1.37.5", + "vue": "^3.2.2", + "vue-composable": "^1.0.0-beta.23", + "vue-i18n": "^9.2.0-alpha.9", + "vue-logger-plugin": "^2.1.3", + "vue-router": "^4.0.10" + }, + "devDependencies": { + "@babel/core": "^7.15.0", + "@babel/preset-typescript": "^7.15.0", + "@fullcalendar/core": "^5.9.0", + "@fullcalendar/daygrid": "^5.9.0", + "@fullcalendar/interaction": "^5.9.0", + "@fullcalendar/timegrid": "^5.9.0", + "@fullcalendar/vue3": "^5.9.0", + "@intlify/vite-plugin-vue-i18n": "^2.4.0", + "@jest/create-cache-key-function": "^26.0.0", + "@types/jest": "^26.0.0", + "@typescript-eslint/eslint-plugin": "~4", + "@typescript-eslint/parser": "~4", + "@vitejs/plugin-vue": "^1.4.0", + "@vue/babel-preset-app": "^4.5.13", + "@vue/cli-plugin-unit-jest": "^4.5.13", + "@vue/compiler-sfc": "^3.2.2", + "@vue/eslint-config-standard": "~6.1", + "@vue/eslint-config-typescript": "~7.0", + "@vue/test-utils": "next", + "@vuedx/typecheck": "~0.7", + "@vuedx/typescript-plugin-vue": "~0.7", + "autoprefixer": "~10.3", + "babel-jest": "^26.0.0", + "cypress": "~8.2", + "eslint": "~7", + "eslint-plugin-import": "^2.24.0", + "eslint-plugin-node": "^11.1.0", + "eslint-plugin-promise": "^5.1.0", + "eslint-plugin-vue": "~7", + "jest": "^26.0.0", + "postcss": "~8.3", + "postcss-nesting": "~8.0", + "rxjs": "^7.3.0", + "ts-jest": "^26.0.0", + "typescript": "~4.3", + "vite": "^2.5.0-beta.1", + "vite-plugin-eslint": "^1.3.0", + "vue-jest": "next", + "wait-on": "~6.0" + }, + "dependenciesMeta": { + "primeflex": { + "unplugged": true + } + } +} diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..b94bad6 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + autoprefixer: {}, + 'postcss-nesting': {}, + }, +} diff --git a/public/assets/logo.png b/public/assets/logo.png new file mode 100644 index 0000000..f3d2503 Binary files /dev/null and b/public/assets/logo.png differ diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000..df36fcf Binary files /dev/null and b/public/favicon.ico differ diff --git a/src/App.scss b/src/App.scss new file mode 100644 index 0000000..082998c --- /dev/null +++ b/src/App.scss @@ -0,0 +1,6 @@ +$gutter: 1rem; + +@import "node_modules/primeflex/primeflex.scss"; + +@import "sass/layout"; +@import "sass/theme"; diff --git a/src/App.vue b/src/App.vue new file mode 100644 index 0000000..f4ce9fc --- /dev/null +++ b/src/App.vue @@ -0,0 +1,26 @@ + + + + + + diff --git a/src/components/Blocks.vue b/src/components/Blocks.vue new file mode 100644 index 0000000..6efd0cc --- /dev/null +++ b/src/components/Blocks.vue @@ -0,0 +1,74 @@ + + + + + + diff --git a/src/components/Blueprint.vue b/src/components/Blueprint.vue new file mode 100644 index 0000000..33c8660 --- /dev/null +++ b/src/components/Blueprint.vue @@ -0,0 +1,10 @@ + + + + + diff --git a/src/components/Home.vue b/src/components/Home.vue new file mode 100644 index 0000000..a50561f --- /dev/null +++ b/src/components/Home.vue @@ -0,0 +1,35 @@ + + + + + + diff --git a/src/components/Store.vue b/src/components/Store.vue new file mode 100644 index 0000000..8b6bb3d --- /dev/null +++ b/src/components/Store.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/src/env.d.ts b/src/env.d.ts new file mode 100644 index 0000000..41c1aa3 --- /dev/null +++ b/src/env.d.ts @@ -0,0 +1,4 @@ +interface ImportMetaEnv { + VITE_APP_VERSION: string + VITE_APP_BUILD_EPOCH: number +} diff --git a/src/main.ts b/src/main.ts new file mode 100644 index 0000000..0b5a5ae --- /dev/null +++ b/src/main.ts @@ -0,0 +1,219 @@ +import { createApp, h } from 'vue'; + +import router from './router' +import {createPinia} from "pinia"; + +import App from './App.vue'; + +import { createHead } from '@vueuse/head' +const head = createHead() + +import PrimeVue from 'primevue/config'; +import AutoComplete from 'primevue/autocomplete'; +import Accordion from 'primevue/accordion'; +import AccordionTab from 'primevue/accordiontab'; +import Avatar from 'primevue/avatar'; +import AvatarGroup from 'primevue/avatargroup'; +import Badge from 'primevue/badge'; +import BadgeDirective from 'primevue/badgedirective'; +import Button from 'primevue/button'; +import Breadcrumb from 'primevue/breadcrumb'; +import Calendar from 'primevue/calendar'; +import Card from 'primevue/card'; +import Carousel from 'primevue/carousel'; +import Chart from 'primevue/chart'; +import Checkbox from 'primevue/checkbox'; +import Chip from 'primevue/chip'; +import Chips from 'primevue/chips'; +import ColorPicker from 'primevue/colorpicker'; +import Column from 'primevue/column'; +import ConfirmDialog from 'primevue/confirmdialog'; +import ConfirmPopup from 'primevue/confirmpopup'; +import ConfirmationService from 'primevue/confirmationservice'; +import ContextMenu from 'primevue/contextmenu'; +import DataTable from 'primevue/datatable'; +import DataView from 'primevue/dataview'; +import DataViewLayoutOptions from 'primevue/dataviewlayoutoptions'; +import Dialog from 'primevue/dialog'; +import Divider from 'primevue/divider'; +import Dropdown from 'primevue/dropdown'; +import Fieldset from 'primevue/fieldset'; +import FileUpload from 'primevue/fileupload'; +import FullCalendar from 'primevue/fullcalendar'; +import InlineMessage from 'primevue/inlinemessage'; +import Inplace from 'primevue/inplace'; +import InputMask from 'primevue/inputmask'; +import InputNumber from 'primevue/inputnumber'; +import InputSwitch from 'primevue/inputswitch'; +import InputText from 'primevue/inputtext'; +import Knob from 'primevue/knob'; +import Galleria from 'primevue/galleria'; +import Listbox from 'primevue/listbox'; +import MegaMenu from 'primevue/megamenu'; +import Menu from 'primevue/menu'; +import Menubar from 'primevue/menubar'; +import Message from 'primevue/message'; +import MultiSelect from 'primevue/multiselect'; +import OrderList from 'primevue/orderlist'; +import OrganizationChart from 'primevue/organizationchart'; +import OverlayPanel from 'primevue/overlaypanel'; +import Paginator from 'primevue/paginator'; +import Panel from 'primevue/panel'; +import PanelMenu from 'primevue/panelmenu'; +import Password from 'primevue/password'; +import PickList from 'primevue/picklist'; +import ProgressBar from 'primevue/progressbar'; +import Rating from 'primevue/rating'; +import RadioButton from 'primevue/radiobutton'; +import Ripple from 'primevue/ripple'; +import SelectButton from 'primevue/selectbutton'; +import ScrollPanel from 'primevue/scrollpanel'; +import ScrollTop from 'primevue/scrolltop'; +import Slider from 'primevue/slider'; +import Sidebar from 'primevue/sidebar'; +import Skeleton from 'primevue/skeleton'; +import SplitButton from 'primevue/splitbutton'; +import Splitter from 'primevue/splitter'; +import SplitterPanel from 'primevue/splitterpanel'; +import Steps from 'primevue/steps'; +import TabMenu from 'primevue/tabmenu'; +import Tag from 'primevue/tag'; +import TieredMenu from 'primevue/tieredmenu'; +import Textarea from 'primevue/textarea'; +import Timeline from 'primevue/timeline'; +import Toast from 'primevue/toast'; +import ToastService from 'primevue/toastservice'; +import Toolbar from 'primevue/toolbar'; +import TabView from 'primevue/tabview'; +import TabPanel from 'primevue/tabpanel'; +import Tooltip from 'primevue/tooltip'; +import ToggleButton from 'primevue/togglebutton'; +import Tree from 'primevue/tree'; +import TreeTable from 'primevue/treetable'; +import TriStateCheckbox from 'primevue/tristatecheckbox'; + +import 'primevue/resources/primevue.min.css'; +import 'primeicons/primeicons.css'; + +import NotFound from '@/pages/NotFound.vue'; +import Access from '@/pages/Access.vue'; +import Error from '@/pages/Error.vue'; + +router.beforeEach(function(to, from, next) { + window.scrollTo(0, 0); + next(); +}); + +const app = createApp({ + computed: { + ViewComponent () { + switch (this.$route.path) { + case '/error': + return Error; + case '/access': + return Access; + case '/notfound': + return NotFound; + default: + return App; + } + } + }, + render () { return h(this.ViewComponent) } +}) + +app.directive('tooltip', Tooltip); +app.directive('ripple', Ripple); +app.directive('badge', BadgeDirective); + +app.component('Accordion', Accordion); +app.component('AccordionTab', AccordionTab); +app.component('AutoComplete', AutoComplete); +app.component('Avatar', Avatar); +app.component('AvatarGroup', AvatarGroup); +app.component('Badge', Badge); +app.component('Breadcrumb', Breadcrumb); +app.component('Button', Button); +app.component('Calendar', Calendar); +app.component('Card', Card); +app.component('Carousel', Carousel); +app.component('Chart', Chart); +app.component('Checkbox', Checkbox); +app.component('Chip', Chip); +app.component('Chips', Chips); +app.component('ColorPicker', ColorPicker); +app.component('Column', Column); +app.component('ConfirmDialog', ConfirmDialog); +app.component('ConfirmPopup', ConfirmPopup); +app.component('ContextMenu', ContextMenu); +app.component('DataTable', DataTable); +app.component('DataView', DataView); +app.component('DataViewLayoutOptions', DataViewLayoutOptions); +app.component('Dialog', Dialog); +app.component('Divider', Divider); +app.component('Dropdown', Dropdown); +app.component('Fieldset', Fieldset); +app.component('FileUpload', FileUpload); +app.component('FullCalendar', FullCalendar); +app.component('InlineMessage', InlineMessage); +app.component('Inplace', Inplace); +app.component('InputMask', InputMask); +app.component('InputNumber', InputNumber); +app.component('InputSwitch', InputSwitch); +app.component('InputText', InputText); +app.component('Galleria', Galleria); +app.component('Knob', Knob); +app.component('Listbox', Listbox); +app.component('MegaMenu', MegaMenu); +app.component('Menu', Menu); +app.component('Menubar', Menubar); +app.component('Message', Message); +app.component('MultiSelect', MultiSelect); +app.component('OrderList', OrderList); +app.component('OrganizationChart', OrganizationChart); +app.component('OverlayPanel', OverlayPanel); +app.component('Paginator', Paginator); +app.component('Panel', Panel); +app.component('PanelMenu', PanelMenu); +app.component('Password', Password); +app.component('PickList', PickList); +app.component('ProgressBar', ProgressBar); +app.component('RadioButton', RadioButton); +app.component('Rating', Rating); +app.component('SelectButton', SelectButton); +app.component('ScrollPanel', ScrollPanel); +app.component('ScrollTop', ScrollTop); +app.component('Slider', Slider); +app.component('Sidebar', Sidebar); +app.component('Skeleton', Skeleton); +app.component('SplitButton', SplitButton); +app.component('Splitter', Splitter); +app.component('SplitterPanel', SplitterPanel); +app.component('Steps', Steps); +app.component('TabMenu', TabMenu); +app.component('TabView', TabView); +app.component('TabPanel', TabPanel); +app.component('Tag', Tag); +app.component('Textarea', Textarea); +app.component('TieredMenu', TieredMenu); +app.component('Timeline', Timeline); +app.component('Toast', Toast); +app.component('Toolbar', Toolbar); +app.component('ToggleButton', ToggleButton); +app.component('Tree', Tree); +app.component('TreeTable', TreeTable); +app.component('TriStateCheckbox', TriStateCheckbox); + + +app.use(createPinia()) + +app.use(head) +app.use(PrimeVue, { ripple: true }); +app.use(ConfirmationService); +app.use(ToastService); +app.use(router) + + + + +app.mount('#app') diff --git a/src/pages/Access.vue b/src/pages/Access.vue new file mode 100644 index 0000000..2700df5 --- /dev/null +++ b/src/pages/Access.vue @@ -0,0 +1,9 @@ + + + + diff --git a/src/pages/Error.vue b/src/pages/Error.vue new file mode 100644 index 0000000..2700df5 --- /dev/null +++ b/src/pages/Error.vue @@ -0,0 +1,9 @@ + + + + diff --git a/src/pages/NotFound.vue b/src/pages/NotFound.vue new file mode 100644 index 0000000..2700df5 --- /dev/null +++ b/src/pages/NotFound.vue @@ -0,0 +1,9 @@ + + + + diff --git a/src/router/index.ts b/src/router/index.ts new file mode 100644 index 0000000..b1b46d0 --- /dev/null +++ b/src/router/index.ts @@ -0,0 +1,32 @@ +import {createRouter, createWebHistory, RouteRecordRaw} from 'vue-router' + +const routes: Array = [ + { + path: '/', + name: 'home', + component: () => import('../components/Home.vue'), + }, + { + path: '/blocks', + name: 'blocks', + component: () => import('../components/Blocks.vue'), + }, + { + path: '/store', + name: 'store', + component: () => import('../components/Store.vue'), + }, + { + path: '/blueprint', + name: 'blueprint', + component: () => import('../components/Blueprint.vue'), + }, + +]; + +const router = createRouter({ + history: createWebHistory(import.meta.env.BASE_URL), + routes +}) + +export default router diff --git a/src/sass/layout.scss b/src/sass/layout.scss new file mode 100644 index 0000000..68f1313 --- /dev/null +++ b/src/sass/layout.scss @@ -0,0 +1 @@ +/* Add your customizations of the layout styles here */ diff --git a/src/sass/theme.scss b/src/sass/theme.scss new file mode 100644 index 0000000..5406a82 --- /dev/null +++ b/src/sass/theme.scss @@ -0,0 +1 @@ +/* Add your customizations of the theme styles here */ diff --git a/src/shims-vue.d.ts b/src/shims-vue.d.ts new file mode 100644 index 0000000..c458ab5 --- /dev/null +++ b/src/shims-vue.d.ts @@ -0,0 +1,6 @@ +declare module '*.vue' { + import { DefineComponent } from 'vue' + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types + const component: DefineComponent<{}, {}, any> + export default component +} diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 0000000..4baa910 --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,2 @@ +export { useMainStore } from './main'; + diff --git a/src/store/main.ts b/src/store/main.ts new file mode 100644 index 0000000..00a77ee --- /dev/null +++ b/src/store/main.ts @@ -0,0 +1,24 @@ +import { defineStore } from 'pinia' + +// main is the name of the store. It is unique across your application +// and will appear in devtools +export const useMainStore = defineStore('main', { + // a function that returns a fresh state + state: () => ({ + counter: 42, + name: 'Eduardo', + }), + // optional getters + getters: { + doubleCount: (state) => { + return state.counter * 2; + }, + }, + // optional actions + actions: { + reset() { + // `this` is the store instance + this.counter = 0 + }, + }, +}) diff --git a/src/widgets/AdvertiseBox.vue b/src/widgets/AdvertiseBox.vue new file mode 100644 index 0000000..0baab5b --- /dev/null +++ b/src/widgets/AdvertiseBox.vue @@ -0,0 +1,22 @@ + + + + + diff --git a/tests/e2e/examples/actions.spec.js b/tests/e2e/examples/actions.spec.js new file mode 100644 index 0000000..ef430ed --- /dev/null +++ b/tests/e2e/examples/actions.spec.js @@ -0,0 +1,299 @@ +/// + +context('Actions', () => { + beforeEach(() => { + cy.visit('https://example.cypress.io/commands/actions') + }) + + // https://on.cypress.io/interacting-with-elements + + it('.type() - type into a DOM element', () => { + // https://on.cypress.io/type + cy.get('.action-email') + .type('fake@email.com').should('have.value', 'fake@email.com') + + // .type() with special character sequences + .type('{leftarrow}{rightarrow}{uparrow}{downarrow}') + .type('{del}{selectall}{backspace}') + + // .type() with key modifiers + .type('{alt}{option}') //these are equivalent + .type('{ctrl}{control}') //these are equivalent + .type('{meta}{command}{cmd}') //these are equivalent + .type('{shift}') + + // Delay each keypress by 0.1 sec + .type('slow.typing@email.com', { delay: 100 }) + .should('have.value', 'slow.typing@email.com') + + cy.get('.action-disabled') + // Ignore error checking prior to type + // like whether the input is visible or disabled + .type('disabled error checking', { force: true }) + .should('have.value', 'disabled error checking') + }) + + it('.focus() - focus on a DOM element', () => { + // https://on.cypress.io/focus + cy.get('.action-focus').focus() + .should('have.class', 'focus') + .prev().should('have.attr', 'style', 'color: orange;') + }) + + it('.blur() - blur off a DOM element', () => { + // https://on.cypress.io/blur + cy.get('.action-blur').type('About to blur').blur() + .should('have.class', 'error') + .prev().should('have.attr', 'style', 'color: red;') + }) + + it('.clear() - clears an input or textarea element', () => { + // https://on.cypress.io/clear + cy.get('.action-clear').type('Clear this text') + .should('have.value', 'Clear this text') + .clear() + .should('have.value', '') + }) + + it('.submit() - submit a form', () => { + // https://on.cypress.io/submit + cy.get('.action-form') + .find('[type="text"]').type('HALFOFF') + + cy.get('.action-form').submit() + .next().should('contain', 'Your form has been submitted!') + }) + + it('.click() - click on a DOM element', () => { + // https://on.cypress.io/click + cy.get('.action-btn').click() + + // You can click on 9 specific positions of an element: + // ----------------------------------- + // | topLeft top topRight | + // | | + // | | + // | | + // | left center right | + // | | + // | | + // | | + // | bottomLeft bottom bottomRight | + // ----------------------------------- + + // clicking in the center of the element is the default + cy.get('#action-canvas').click() + + cy.get('#action-canvas').click('topLeft') + cy.get('#action-canvas').click('top') + cy.get('#action-canvas').click('topRight') + cy.get('#action-canvas').click('left') + cy.get('#action-canvas').click('right') + cy.get('#action-canvas').click('bottomLeft') + cy.get('#action-canvas').click('bottom') + cy.get('#action-canvas').click('bottomRight') + + // .click() accepts an x and y coordinate + // that controls where the click occurs :) + + cy.get('#action-canvas') + .click(80, 75) // click 80px on x coord and 75px on y coord + .click(170, 75) + .click(80, 165) + .click(100, 185) + .click(125, 190) + .click(150, 185) + .click(170, 165) + + // click multiple elements by passing multiple: true + cy.get('.action-labels>.label').click({ multiple: true }) + + // Ignore error checking prior to clicking + cy.get('.action-opacity>.btn').click({ force: true }) + }) + + it('.dblclick() - double click on a DOM element', () => { + // https://on.cypress.io/dblclick + + // Our app has a listener on 'dblclick' event in our 'scripts.js' + // that hides the div and shows an input on double click + cy.get('.action-div').dblclick().should('not.be.visible') + cy.get('.action-input-hidden').should('be.visible') + }) + + it('.rightclick() - right click on a DOM element', () => { + // https://on.cypress.io/rightclick + + // Our app has a listener on 'contextmenu' event in our 'scripts.js' + // that hides the div and shows an input on right click + cy.get('.rightclick-action-div').rightclick().should('not.be.visible') + cy.get('.rightclick-action-input-hidden').should('be.visible') + }) + + it('.check() - check a checkbox or radio element', () => { + // https://on.cypress.io/check + + // By default, .check() will check all + // matching checkbox or radio elements in succession, one after another + cy.get('.action-checkboxes [type="checkbox"]').not('[disabled]') + .check().should('be.checked') + + cy.get('.action-radios [type="radio"]').not('[disabled]') + .check().should('be.checked') + + // .check() accepts a value argument + cy.get('.action-radios [type="radio"]') + .check('radio1').should('be.checked') + + // .check() accepts an array of values + cy.get('.action-multiple-checkboxes [type="checkbox"]') + .check(['checkbox1', 'checkbox2']).should('be.checked') + + // Ignore error checking prior to checking + cy.get('.action-checkboxes [disabled]') + .check({ force: true }).should('be.checked') + + cy.get('.action-radios [type="radio"]') + .check('radio3', { force: true }).should('be.checked') + }) + + it('.uncheck() - uncheck a checkbox element', () => { + // https://on.cypress.io/uncheck + + // By default, .uncheck() will uncheck all matching + // checkbox elements in succession, one after another + cy.get('.action-check [type="checkbox"]') + .not('[disabled]') + .uncheck().should('not.be.checked') + + // .uncheck() accepts a value argument + cy.get('.action-check [type="checkbox"]') + .check('checkbox1') + .uncheck('checkbox1').should('not.be.checked') + + // .uncheck() accepts an array of values + cy.get('.action-check [type="checkbox"]') + .check(['checkbox1', 'checkbox3']) + .uncheck(['checkbox1', 'checkbox3']).should('not.be.checked') + + // Ignore error checking prior to unchecking + cy.get('.action-check [disabled]') + .uncheck({ force: true }).should('not.be.checked') + }) + + it('.select() - select an option in a