plum-e2e 1.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/.husky/pre-commit +2 -0
  2. package/.prettierignore +21 -0
  3. package/.prettierrc +15 -0
  4. package/LICENSE +677 -0
  5. package/README.md +5 -0
  6. package/backend/Dockerfile +30 -0
  7. package/backend/README.md +0 -0
  8. package/backend/_scaffold/features/LoginPage.feature +6 -0
  9. package/backend/_scaffold/pages/LoginPage.js +22 -0
  10. package/backend/_scaffold/step_definitions/LoginSteps.js +9 -0
  11. package/backend/_scaffold/utils/constants.js +3 -0
  12. package/backend/_scaffold/utils/hooks.js +65 -0
  13. package/backend/_scaffold/utils/utils.js +10 -0
  14. package/backend/app.js +37 -0
  15. package/backend/config/scripts/create-settings.js +60 -0
  16. package/backend/config/scripts/generate-report.js +135 -0
  17. package/backend/config/scripts/run-tests.js +37 -0
  18. package/backend/cucumber.json +6 -0
  19. package/backend/package-lock.json +3403 -0
  20. package/backend/package.json +29 -0
  21. package/backend/playwright.config.js +97 -0
  22. package/backend/routes/cron.routes.js +127 -0
  23. package/backend/routes/reports.routes.js +42 -0
  24. package/backend/routes/schedules.routes.js +32 -0
  25. package/backend/routes/tests.routes.js +33 -0
  26. package/backend/server.js +39 -0
  27. package/backend/services/cronService.js +127 -0
  28. package/backend/services/envService.js +43 -0
  29. package/backend/services/reportService.js +50 -0
  30. package/backend/services/scheduleService.js +34 -0
  31. package/backend/services/testService.js +70 -0
  32. package/backend/websockets/socketHandler.js +46 -0
  33. package/bin/plum.js +188 -0
  34. package/docker-compose.yml +41 -0
  35. package/frontend/Dockerfile +26 -0
  36. package/frontend/README.md +0 -0
  37. package/frontend/jsconfig.json +13 -0
  38. package/frontend/package-lock.json +2894 -0
  39. package/frontend/package.json +26 -0
  40. package/frontend/postcss.config.js +23 -0
  41. package/frontend/src/app.css +35 -0
  42. package/frontend/src/app.html +28 -0
  43. package/frontend/src/lib/index.js +18 -0
  44. package/frontend/src/routes/+layout.svelte +34 -0
  45. package/frontend/src/routes/+page.svelte +188 -0
  46. package/frontend/src/routes/components/Navigation.svelte +53 -0
  47. package/frontend/src/routes/reports/+page.svelte +160 -0
  48. package/frontend/src/routes/scheduled-tests/+page.svelte +363 -0
  49. package/frontend/static/favicon.png +0 -0
  50. package/frontend/svelte.config.js +30 -0
  51. package/frontend/tailwind.config.js +44 -0
  52. package/frontend/vite.config.js +23 -0
  53. package/license-config.json +37 -0
  54. package/package.json +28 -0
  55. package/resources/comments-format.text +23 -0
  56. package/resources/gpl-3.0-license.txt +14 -0
@@ -0,0 +1,70 @@
1
+ /*
2
+ * This file is part of Plum.
3
+ *
4
+ * Plum 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
+ * Plum 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 Plum. If not, see https://www.gnu.org/licenses/.
16
+ */
17
+
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+
21
+ const FEATURES_DIR = path.join(__dirname, '../tests/features');
22
+
23
+ const getTestSuites = () => {
24
+ const suites = [];
25
+
26
+ const files = fs.readdirSync(FEATURES_DIR).filter((file) => file.endsWith('.feature'));
27
+
28
+ files.forEach((file) => {
29
+ const content = fs.readFileSync(path.join(FEATURES_DIR, file), 'utf8');
30
+
31
+ let suiteName = '';
32
+ let suiteTags = [];
33
+ const tests = [];
34
+
35
+ // Suites Tag Extractor
36
+ const featureMatch = content.match(/Feature:\s*(.+)/);
37
+ if (featureMatch) {
38
+ suiteName = featureMatch[1].trim();
39
+ }
40
+
41
+ const suiteTagsMatch = content.match(/^(@[^\n]+)/m);
42
+ if (suiteTagsMatch) {
43
+ suiteTags = suiteTagsMatch[0].split(/\s+/).filter((tag) => tag.trim() !== ''); // Remove empty elements
44
+ }
45
+
46
+ // Test Cases Tag Extractor
47
+ const scenarioMatches = [...content.matchAll(/(@[^\n]+)\s*Scenario:\s*(.+)/g)];
48
+ scenarioMatches.forEach((match) => {
49
+ const tags = match[1].split(/\s+/).filter((tag) => tag.trim() !== ''); // Remove empty elements
50
+ const scenarioText = match[2].trim();
51
+
52
+ if (tags.length === 0) return; // Skip if there are no valid tags
53
+
54
+ const testId = tags.shift(); // Extract the first tag as testId
55
+
56
+ tests.push({
57
+ id: tags.length > 0 ? [testId, ...tags] : testId, // Ensure testId is correctly structured
58
+ testCase: scenarioText
59
+ });
60
+ });
61
+
62
+ if (suiteName && tests.length) {
63
+ suites.push({ suiteName, suiteId: suiteTags.length > 1 ? suiteTags : suiteTags[0], tests });
64
+ }
65
+ });
66
+
67
+ return { suites };
68
+ };
69
+
70
+ module.exports = { getTestSuites };
@@ -0,0 +1,46 @@
1
+ /*
2
+ * This file is part of Plum.
3
+ *
4
+ * Plum 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
+ * Plum 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 Plum. If not, see https://www.gnu.org/licenses/.
16
+ */
17
+
18
+ const { spawn } = require('child_process');
19
+
20
+ const socketHandler = (io) => {
21
+ io.on('connection', (socket) => {
22
+ console.log('WebSocket connection established');
23
+ socket.on('run-test', (testID) => {
24
+ const tag = testID ? `${testID}` : '';
25
+
26
+ const testProcess = spawn('npm', ['run', 'test'], {
27
+ env: { ...process.env, TAG: tag, TRIGGER: 'manual-trigger' }
28
+ });
29
+
30
+ testProcess.stdout.on('data', (data) => {
31
+ socket.emit('log', data.toString());
32
+ });
33
+
34
+ testProcess.stderr.on('data', (data) => {
35
+ socket.emit('log', `[ERROR] ${data.toString()}`);
36
+ });
37
+
38
+ testProcess.on('close', (code) => {
39
+ socket.emit('log', `Test finished with code ${code}`);
40
+ socket.emit('done');
41
+ });
42
+ });
43
+ });
44
+ };
45
+
46
+ module.exports = socketHandler;
package/bin/plum.js ADDED
@@ -0,0 +1,188 @@
1
+ #!/usr/bin/env node
2
+ /*
3
+ * This file is part of Plum.
4
+ *
5
+ * Plum is free software: you can redistribute it and/or modify
6
+ * it under the terms of the GNU General Public License as published by
7
+ * the Free Software Foundation, either version 3 of the License, or
8
+ * (at your option) any later version.
9
+ *
10
+ * Plum is distributed in the hope that it will be useful,
11
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
+ * GNU General Public License for more details.
14
+ *
15
+ * You should have received a copy of the GNU General Public License
16
+ * along with Plum. If not, see https://www.gnu.org/licenses/.
17
+ */
18
+
19
+ import { execSync } from 'child_process';
20
+ import fs from 'fs';
21
+ import path from 'path';
22
+ import { fileURLToPath } from 'url';
23
+ import fse from 'fs-extra';
24
+
25
+ const __filename = fileURLToPath(import.meta.url);
26
+ const __dirname = path.dirname(__filename);
27
+ const command = process.argv[2];
28
+ const plumRoot = path.resolve(__dirname, '..');
29
+ const userTestsPath = path.join(process.cwd(), 'tests');
30
+ const scaffoldTestsPath = path.join(plumRoot, 'backend', '_scaffold');
31
+ const overrideFilePath = path.join(plumRoot, 'docker-compose.override.yml');
32
+
33
+ // Paths for .env file
34
+ const rootEnvPath = path.join(process.cwd(), '.env');
35
+ const backendEnvPath = path.join(plumRoot, 'backend', '.env');
36
+
37
+ // Function to create the .env file with default values NOTE: DO NOT FORMAT envContent
38
+ function createEnvFile() {
39
+ const envFilePath = path.join(process.cwd(), '.env');
40
+
41
+ // Check if .env file already exists
42
+ if (fs.existsSync(envFilePath)) {
43
+ copyEnvFile();
44
+ console.log('⚠️ .env file already exists. Syncing .env file...\n');
45
+ return; // Exit if file exists
46
+ }
47
+
48
+ // Default content for .env file
49
+ const envContent = `BASE_URL=https://www.saucedemo.com/v1/
50
+ IS_HEADLESS=false
51
+ `;
52
+
53
+ // Write the content to the .env file
54
+ fs.writeFileSync(envFilePath, envContent, 'utf8');
55
+ console.log('✅ .env file created with default values.\n');
56
+ }
57
+
58
+ // Function to copy .env file from root to backend
59
+ function copyEnvFile() {
60
+ try {
61
+ if (fs.existsSync(rootEnvPath)) {
62
+ fse.copySync(rootEnvPath, backendEnvPath);
63
+ console.log('.env file copied to the backend folder.\n');
64
+ } else {
65
+ console.log('.env file not found in the root directory.\n');
66
+ }
67
+ } catch (err) {
68
+ console.error('Error copying .env file:', err);
69
+ }
70
+ }
71
+
72
+ /* -----------------------------------------------------
73
+ * Commands
74
+ * Description:
75
+ * Main command line interface for Plum. Use
76
+ * "plum <command>" to run the desired command.
77
+ * ------------------------------------------------------ */
78
+ switch (command) {
79
+ case 'init':
80
+ console.log('--------------------------------------');
81
+ console.log('🟣 Initializing Plum...\n');
82
+
83
+ if (fs.existsSync(userTestsPath)) {
84
+ console.log('⚠️ A `tests/` folder already exists.\n');
85
+ } else {
86
+ console.log('🧪 Creating test scaffold...\n');
87
+ fse.copySync(scaffoldTestsPath, userTestsPath);
88
+ console.log('✅ `tests/` initialized with example files.\n');
89
+ }
90
+
91
+ // Create .env file with default values
92
+ createEnvFile();
93
+ console.log(
94
+ '🟣 Plum is now ready!\n\n Scaffold test cases are included in the `tests/` folder.\n For more information about Cucumber, visit: https://cucumber.io/\n\n - To start the server, run:\n `plum start` \n\n - If you are developing locally, run:\n `plum local <@tag/blank if you want to run all tests>`'
95
+ );
96
+ console.log('--------------------------------------');
97
+ break;
98
+
99
+ case 'start':
100
+ console.log('--------------------------------------');
101
+ console.log('🚀 Running plum via Docker Compose...');
102
+
103
+ // Copy .env file from root to backend
104
+ copyEnvFile();
105
+
106
+ // Convert Windows paths to safe format
107
+ const userTestsAbs = path.resolve(process.cwd(), 'tests').replace(/\\/g, '/');
108
+ const userModulesAbs = path.resolve(process.cwd(), 'node_modules').replace(/\\/g, '/');
109
+
110
+ // Generate docker-compose.override.yml
111
+ const overrideYAML = [
112
+ 'services:',
113
+ ' backend:',
114
+ ' volumes:',
115
+ ` - "${userTestsAbs}:/app/tests"`,
116
+ ` - "${userModulesAbs}:/app/tests/node_modules"`
117
+ ].join('\n');
118
+
119
+ fs.writeFileSync(overrideFilePath, overrideYAML + '\n', 'utf8');
120
+ console.log('✅ docker-compose.override.yml written');
121
+
122
+ // Run docker compose
123
+ execSync('docker compose up', {
124
+ cwd: plumRoot,
125
+ stdio: 'inherit'
126
+ });
127
+ console.log('--------------------------------------');
128
+ break;
129
+
130
+ case 'local': {
131
+ console.log('--------------------------------------\n');
132
+ console.log('🚀 Running Plum locally...');
133
+
134
+ // Copy .env file from root to backend
135
+ copyEnvFile();
136
+
137
+ const tagArg = process.argv[3]; // This is your tag, like @test-1
138
+ const userTestsPath = path.resolve(process.cwd(), 'tests');
139
+ const backendTestsPath = path.join(plumRoot, 'backend', 'tests');
140
+
141
+ // Copy user tests into backend
142
+ if (fs.existsSync(userTestsPath)) {
143
+ console.log('📦 Syncing your tests...');
144
+ fse.copySync(userTestsPath, backendTestsPath);
145
+ } else {
146
+ console.log('⚠️ No `tests/` folder found in the user directory.');
147
+ }
148
+
149
+ // Run npm install
150
+ console.log('--------------------------------------\n');
151
+ console.log('Running `npm install`...');
152
+
153
+ execSync('npm install', {
154
+ cwd: path.join(plumRoot, 'backend'),
155
+ stdio: 'inherit'
156
+ });
157
+
158
+ console.log('Running `npx playwright install`...');
159
+
160
+ execSync('npx playwright install', {
161
+ cwd: path.join(plumRoot, 'backend'),
162
+ stdio: 'inherit'
163
+ });
164
+
165
+ // Run the tests with the tag filter, only if a tag is provided
166
+ console.log('--------------------------------------\n');
167
+ console.log('Running `npm run test` with:');
168
+ console.log('TAG =', tagArg ?? '');
169
+ console.log('TRIGGER =', 'command-line-trigger');
170
+
171
+ execSync('npm run test', {
172
+ cwd: path.join(plumRoot, 'backend'),
173
+ stdio: 'inherit',
174
+ env: {
175
+ ...process.env,
176
+ TAG: tagArg ?? '',
177
+ TRIGGER: 'command-line-trigger'
178
+ }
179
+ });
180
+ console.log('--------------------------------------');
181
+ break;
182
+ }
183
+
184
+ default:
185
+ console.log('--------------------------------------');
186
+ console.log('Usage: plum <init|start|local>');
187
+ console.log('--------------------------------------');
188
+ }
@@ -0,0 +1,41 @@
1
+ #
2
+ # This file is part of Plum.
3
+ #
4
+ # Plum 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
+ # Plum 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 Plum. If not, see https://www.gnu.org/licenses/.
16
+ #
17
+
18
+ services:
19
+ backend:
20
+ build: ./backend
21
+ ports:
22
+ - '3001:3001'
23
+ volumes:
24
+ - ./backend/reports:/app/reports
25
+ - ./backend/config:/app/config
26
+ - ./backend/tests:/app/tests:rw
27
+ networks:
28
+ - app-network
29
+
30
+ frontend:
31
+ build: ./frontend
32
+ ports:
33
+ - '5173:5173'
34
+ depends_on:
35
+ - backend
36
+ networks:
37
+ - app-network
38
+
39
+ networks:
40
+ app-network:
41
+ driver: bridge
@@ -0,0 +1,26 @@
1
+ #
2
+ # This file is part of Plum.
3
+ #
4
+ # Plum 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
+ # Plum 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 Plum. If not, see https://www.gnu.org/licenses/.
16
+ #
17
+
18
+ FROM node:18-alpine
19
+ WORKDIR /app
20
+
21
+ COPY package.json package-lock.json ./
22
+ RUN npm install
23
+
24
+ COPY . .
25
+ EXPOSE 5173
26
+ CMD ["npm", "run", "dev", "--", "--host"]
File without changes
@@ -0,0 +1,13 @@
1
+ {
2
+ "extends": "./.svelte-kit/tsconfig.json",
3
+ "compilerOptions": {
4
+ "allowJs": true,
5
+ "checkJs": false,
6
+ "moduleResolution": "bundler"
7
+ }
8
+ // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
9
+ // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
10
+ //
11
+ // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
12
+ // from the referenced tsconfig.json - TypeScript does not merge them in
13
+ }