element with the "row" class', () => {
8 | const wrapper = mount(App, {
9 | });
10 | const columns = wrapper.findAll('div.row');
11 | expect(columns.length).toBe(1);
12 | });
13 |
14 | it('should have
elements with the "column" class', () => {
15 | const wrapper = mount(App, {
16 | });
17 | const columns = wrapper.findAll('div.column');
18 | expect(columns.length).toBe(2);
19 | });
20 |
21 | it('the first column should have a
with the title "Charcuterie Board"', () => {
22 | const wrapper = mount(App, {
23 | });
24 | const columns = wrapper.findAll('div.column');
25 | const header = columns[0].find('h1');
26 | expect(header.text()).toBe('Charcuterie Board');
27 | });
28 |
29 | it('the second column should have a with the title "Inventory"', () => {
30 | const wrapper = mount(App, {
31 | });
32 | const columns = wrapper.findAll('div.column');
33 | const header = columns[1].find('h1');
34 | expect(header.text()).toBe('Inventory');
35 | });
36 |
37 | it('should have the following components: , & ', () => {
38 | const wrapper = mount(App, {
39 | shallow: true
40 | });
41 | const components = ['CharcuterieBoardLogo', 'CharcuterieBoard', 'CharcuterieInventory'];
42 | components.forEach((name) => {
43 | const component = wrapper.find(kebabCase(name) + '-stub');
44 | expect(component.exists()).toBe(true);
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | TAG?=18-alpine
2 | CONTAINER?=$(shell basename $(CURDIR))
3 | IMAGE_INFO=$(shell docker image inspect $(CONTAINER):$(TAG))
4 | IMAGE_NAME=${CONTAINER}:${TAG}
5 | DOCKER_RUN=docker container run --rm -it -v `pwd`:/app
6 |
7 | .PHONY: build clean dev image-build image-check npm ssh test test-coverage test-ui
8 |
9 | # Perform a dist build via npm run build
10 | build: image-check
11 | ${DOCKER_RUN} --name ${CONTAINER}-$@ ${IMAGE_NAME} run build
12 | # Remove node_modules/ & package-lock.json
13 | clean:
14 | rm -rf node_modules/
15 | rm -f package-lock.json
16 | # Run the development server via npm run dev
17 | dev: image-check
18 | ${DOCKER_RUN} --name ${CONTAINER}-$@ -p 3000:3000 ${IMAGE_NAME} run dev
19 | # Build the Docker image & run npm install
20 | image-build:
21 | docker build . -t ${IMAGE_NAME} --build-arg TAG=${TAG} --no-cache
22 | ${DOCKER_RUN} --name ${CONTAINER}-$@ ${IMAGE_NAME} install
23 | # Ensure the image has been created
24 | image-check:
25 | ifeq ($(IMAGE_INFO), [])
26 | image-check: image-build
27 | endif
28 | # Run the passed in npm command
29 | npm: image-check
30 | ${DOCKER_RUN} --name ${CONTAINER}-$@ ${IMAGE_NAME} $(filter-out $@,$(MAKECMDGOALS)) $(MAKEFLAGS)
31 | # Open a shell inside of the container
32 | ssh: image-check
33 | ${DOCKER_RUN} --name ${CONTAINER}-$@ --entrypoint=/bin/sh ${IMAGE_NAME}
34 | # Run tests via npm run test
35 | test: image-check
36 | ${DOCKER_RUN} --name ${CONTAINER}-$@ ${IMAGE_NAME} run test
37 | # Run tests with coverage via npm run test-coverage
38 | test-coverage: image-check
39 | ${DOCKER_RUN} --name ${CONTAINER}-$@ ${IMAGE_NAME} run test-coverage
40 | # Run tests with the Vitest UI via npm run test-ui
41 | test-ui: image-check
42 | ${DOCKER_RUN} --name ${CONTAINER}-$@ -p 51204:51204 ${IMAGE_NAME} run test-ui
43 | %:
44 | @:
45 | # ref: https://stackoverflow.com/questions/6273608/how-to-pass-argument-to-makefile-from-command-line
46 |
--------------------------------------------------------------------------------
/src/assets/cheese.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/assets/nut.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Charcuterie Vue + Vitest example
2 |
3 | This is the git repo for the project used in the [Test-Driven Development with Vitest](https://craftquest.io/courses/test-driven-development-with-vitest) course on [CraftQuest.io](https://craftquest.io/). The full live stream is here: [CraftQuest on Call 58: TDD with Vitest](https://craftquest.io/livestreams/craftquest-on-call-58-tdd-with-vitest)
4 |
5 | This project uses [Vue 3](https://vuejs.org/), [Vite 4](https://vitejs.dev/), [Vitest](https://vitest.dev/), and [TypeScript](https://www.typescriptlang.org/) to demonstrate test-driven development with Vitest.
6 |
7 | The [`no-code-just-tests`](https://github.com/nystudio107/charcuterie-vue-vitest/tree/no-code-just-tests) branch has just the tests, so you can use TDD to write the code that satisfies them.
8 |
9 | # Requirements
10 |
11 | This project has its devops shrink-wrapped with the project via Docker, and runs inside of a Docker container. You do not need `npm` or `node` installed.
12 |
13 | You'll need only the following installed locally:
14 |
15 | * [Docker Desktop](https://www.docker.com/products/docker-desktop/)
16 |
17 | # Run it in a browser in Github Codespaces
18 |
19 | 1. In [Github](https://github.com/nystudio107/charcuterie-vue-vitest), click on **Use this template** and select **Open in a codespace**
20 | 2. In the resulting Terminal window, type `make dev` to start the project up
21 | 3. Click on the **Open in a Browser** button that appears at the bottom-right
22 |
23 | This lets anyone use the project without having to do _any_ local setup.
24 |
25 | You can use the Codespaces editor to edit files, run tests, or load the site frontend, all from within a browser!
26 |
27 | # Commands
28 |
29 | To use this project, use the following `make` commands from the project root:
30 |
31 | * `make dev` - Run the development server via `npm run dev`
32 | * `make test` - Run tests via `npm run test`
33 | * `make test-coverage` - Run tests with coverage via `npm run test-coverage`
34 | * `make test-ui` - Run tests with the Vitest UI via `npm run test-ui`
35 | * `make build` - Perform a dist build via `npm run build`
36 | * `make npm xxx` - runs the `npm` command passed in, e.g.: `make npm install`
37 | * `make ssh` - Open a shell inside of the container
38 | * `make clean` - Remove node_modules/ & package-lock.json
39 | * `make image-build` - Build the Docker image & run `npm install`
40 |
41 | Port `3000` needs to be available to run the Vite dev server, and port `51204` needs to be available to run the Vitest UI.
42 |
43 | Each command runs in a separate container, so you can use as many of them simultaneously as you want.
44 |
--------------------------------------------------------------------------------
/src/__tests__/__snapshots__/charcuterie-inventory.test.ts.snap:
--------------------------------------------------------------------------------
1 | // Vitest Snapshot v1
2 |
3 | exports[`charcuterie-inventory.ts > should have all of the items from our spec 1`] = `
4 | [
5 | {
6 | "name": "graham",
7 | "type": "cracker",
8 | },
9 | {
10 | "name": "cream",
11 | "type": "cracker",
12 | },
13 | {
14 | "name": "matzo",
15 | "type": "cracker",
16 | },
17 | {
18 | "name": "graham",
19 | "type": "cracker",
20 | },
21 | {
22 | "name": "oatcake",
23 | "type": "cracker",
24 | },
25 | {
26 | "name": "rice",
27 | "type": "cracker",
28 | },
29 | {
30 | "name": "salami",
31 | "type": "meat",
32 | },
33 | {
34 | "name": "soppressata",
35 | "type": "meat",
36 | },
37 | {
38 | "name": "calabrese",
39 | "type": "meat",
40 | },
41 | {
42 | "name": "mortadella",
43 | "type": "meat",
44 | },
45 | {
46 | "name": "prosciutto",
47 | "type": "meat",
48 | },
49 | {
50 | "name": "chorizo",
51 | "type": "meat",
52 | },
53 | {
54 | "name": "capicola",
55 | "type": "meat",
56 | },
57 | {
58 | "name": "cheddar",
59 | "type": "cheese",
60 | },
61 | {
62 | "name": "roquefort",
63 | "type": "cheese",
64 | },
65 | {
66 | "name": "swiss",
67 | "type": "cheese",
68 | },
69 | {
70 | "name": "pecorino-romano",
71 | "type": "cheese",
72 | },
73 | {
74 | "name": "parmigiano-reggiano",
75 | "type": "cheese",
76 | },
77 | {
78 | "name": "gruyere",
79 | "type": "cheese",
80 | },
81 | {
82 | "name": "provolone",
83 | "type": "cheese",
84 | },
85 | {
86 | "name": "peanuts",
87 | "type": "nut",
88 | },
89 | {
90 | "name": "almond",
91 | "type": "nut",
92 | },
93 | {
94 | "name": "cashew",
95 | "type": "nut",
96 | },
97 | {
98 | "name": "walnut",
99 | "type": "nut",
100 | },
101 | {
102 | "name": "brazil",
103 | "type": "nut",
104 | },
105 | {
106 | "name": "pistachio",
107 | "type": "nut",
108 | },
109 | {
110 | "name": "peanuts",
111 | "type": "nut",
112 | },
113 | {
114 | "name": "apple",
115 | "type": "fruit",
116 | },
117 | {
118 | "name": "olives",
119 | "type": "fruit",
120 | },
121 | {
122 | "name": "strawberry",
123 | "type": "fruit",
124 | },
125 | {
126 | "name": "cherry",
127 | "type": "fruit",
128 | },
129 | {
130 | "name": "blackberry",
131 | "type": "fruit",
132 | },
133 | {
134 | "name": "raspberry",
135 | "type": "fruit",
136 | },
137 | {
138 | "name": "pear",
139 | "type": "fruit",
140 | },
141 | {
142 | "name": "carrot",
143 | "type": "vegetable",
144 | },
145 | ]
146 | `;
147 |
--------------------------------------------------------------------------------
/src/assets/meat.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/src/js/charcuterie-inventory.ts:
--------------------------------------------------------------------------------
1 | import {CharcuterieItemType} from '@/@types/charcuterie-enums';
2 |
3 | const charcuterieInventory: CharcuterieItem[] = [
4 | {
5 | name: 'graham',
6 | type: CharcuterieItemType.Cracker
7 | },
8 | {
9 | name: 'cream',
10 | type: CharcuterieItemType.Cracker
11 | },
12 | {
13 | name: 'matzo',
14 | type: CharcuterieItemType.Cracker
15 | },
16 | {
17 | name: 'graham',
18 | type: CharcuterieItemType.Cracker
19 | },
20 | {
21 | name: 'oatcake',
22 | type: CharcuterieItemType.Cracker,
23 | },
24 | {
25 | name: 'rice',
26 | type: CharcuterieItemType.Cracker
27 | },
28 | {
29 | name: 'salami',
30 | type: CharcuterieItemType.Meat,
31 | },
32 | {
33 | name: 'soppressata',
34 | type: CharcuterieItemType.Meat,
35 | },
36 | {
37 | name: 'calabrese',
38 | type: CharcuterieItemType.Meat,
39 | },
40 | {
41 | name: 'mortadella',
42 | type: CharcuterieItemType.Meat,
43 | },
44 | {
45 | name: 'prosciutto',
46 | type: CharcuterieItemType.Meat,
47 | },
48 | {
49 | name: 'chorizo',
50 | type: CharcuterieItemType.Meat,
51 | },
52 | {
53 | name: 'capicola',
54 | type: CharcuterieItemType.Meat,
55 | },
56 | {
57 | name: 'cheddar',
58 | type: CharcuterieItemType.Cheese,
59 | },
60 | {
61 | name: 'roquefort',
62 | type: CharcuterieItemType.Cheese,
63 | },
64 | {
65 | name: 'swiss',
66 | type: CharcuterieItemType.Cheese,
67 | },
68 | {
69 | name: 'pecorino-romano',
70 | type: CharcuterieItemType.Cheese,
71 | },
72 | {
73 | name: 'parmigiano-reggiano',
74 | type: CharcuterieItemType.Cheese,
75 | },
76 | {
77 | name: 'gruyere',
78 | type: CharcuterieItemType.Cheese,
79 | },
80 | {
81 | name: 'provolone',
82 | type: CharcuterieItemType.Cheese,
83 | },
84 | {
85 | name: 'peanuts',
86 | type: CharcuterieItemType.Nut,
87 | },
88 | {
89 | name: 'almond',
90 | type: CharcuterieItemType.Nut,
91 | },
92 | {
93 | name: 'cashew',
94 | type: CharcuterieItemType.Nut,
95 | },
96 | {
97 | name: 'walnut',
98 | type: CharcuterieItemType.Nut,
99 | },
100 | {
101 | name: 'brazil',
102 | type: CharcuterieItemType.Nut,
103 | },
104 | {
105 | name: 'pistachio',
106 | type: CharcuterieItemType.Nut,
107 | },
108 | {
109 | name: 'peanuts',
110 | type: CharcuterieItemType.Nut,
111 | },
112 | {
113 | name: 'apple',
114 | type: CharcuterieItemType.Fruit,
115 | },
116 | {
117 | name: 'olives',
118 | type: CharcuterieItemType.Fruit,
119 | },
120 | {
121 | name: 'strawberry',
122 | type: CharcuterieItemType.Fruit,
123 | },
124 | {
125 | name: 'cherry',
126 | type: CharcuterieItemType.Fruit,
127 | },
128 | {
129 | name: 'blackberry',
130 | type: CharcuterieItemType.Fruit,
131 | },
132 | {
133 | name: 'raspberry',
134 | type: CharcuterieItemType.Fruit,
135 | },
136 | {
137 | name: 'pear',
138 | type: CharcuterieItemType.Fruit,
139 | },
140 | {
141 | name: 'carrot',
142 | type: CharcuterieItemType.Vegetable,
143 | },
144 | ];
145 |
146 | export function getInventory() {
147 | return charcuterieInventory;
148 | }
149 |
150 | export function getInventoryItem(name: string): CharcuterieItem|undefined {
151 | return charcuterieInventory.find((value: CharcuterieItem) => value.name === name);
152 | }
153 |
--------------------------------------------------------------------------------
/src/assets/fruit.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/assets/vegetable.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/assets/cracker.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/src/assets/charcuterie-board-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
53 |
--------------------------------------------------------------------------------