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.
- package/.husky/pre-commit +2 -0
- package/.prettierignore +21 -0
- package/.prettierrc +15 -0
- package/LICENSE +677 -0
- package/README.md +5 -0
- package/backend/Dockerfile +30 -0
- package/backend/README.md +0 -0
- package/backend/_scaffold/features/LoginPage.feature +6 -0
- package/backend/_scaffold/pages/LoginPage.js +22 -0
- package/backend/_scaffold/step_definitions/LoginSteps.js +9 -0
- package/backend/_scaffold/utils/constants.js +3 -0
- package/backend/_scaffold/utils/hooks.js +65 -0
- package/backend/_scaffold/utils/utils.js +10 -0
- package/backend/app.js +37 -0
- package/backend/config/scripts/create-settings.js +60 -0
- package/backend/config/scripts/generate-report.js +135 -0
- package/backend/config/scripts/run-tests.js +37 -0
- package/backend/cucumber.json +6 -0
- package/backend/package-lock.json +3403 -0
- package/backend/package.json +29 -0
- package/backend/playwright.config.js +97 -0
- package/backend/routes/cron.routes.js +127 -0
- package/backend/routes/reports.routes.js +42 -0
- package/backend/routes/schedules.routes.js +32 -0
- package/backend/routes/tests.routes.js +33 -0
- package/backend/server.js +39 -0
- package/backend/services/cronService.js +127 -0
- package/backend/services/envService.js +43 -0
- package/backend/services/reportService.js +50 -0
- package/backend/services/scheduleService.js +34 -0
- package/backend/services/testService.js +70 -0
- package/backend/websockets/socketHandler.js +46 -0
- package/bin/plum.js +188 -0
- package/docker-compose.yml +41 -0
- package/frontend/Dockerfile +26 -0
- package/frontend/README.md +0 -0
- package/frontend/jsconfig.json +13 -0
- package/frontend/package-lock.json +2894 -0
- package/frontend/package.json +26 -0
- package/frontend/postcss.config.js +23 -0
- package/frontend/src/app.css +35 -0
- package/frontend/src/app.html +28 -0
- package/frontend/src/lib/index.js +18 -0
- package/frontend/src/routes/+layout.svelte +34 -0
- package/frontend/src/routes/+page.svelte +188 -0
- package/frontend/src/routes/components/Navigation.svelte +53 -0
- package/frontend/src/routes/reports/+page.svelte +160 -0
- package/frontend/src/routes/scheduled-tests/+page.svelte +363 -0
- package/frontend/static/favicon.png +0 -0
- package/frontend/svelte.config.js +30 -0
- package/frontend/tailwind.config.js +44 -0
- package/frontend/vite.config.js +23 -0
- package/license-config.json +37 -0
- package/package.json +28 -0
- package/resources/comments-format.text +23 -0
- 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
|
+
}
|