Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .env.e2e.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# E2E Testing Environment Configuration
# Copy this file to .env.e2e and modify according to your local setup

# Application URL
E2E_APP_URL=http://localhost:8000

# Database Configuration for E2E Testing
E2E_DB_CONNECTION=mysql
E2E_DB_HOST=localhost
E2E_DB_PORT=3306
E2E_DB_DATABASE=opendesa_test
E2E_DB_USERNAME=root
E2E_DB_PASSWORD=secret

# Authentication Credentials
E2E_ADMIN_EMAIL=admin@test.com
E2E_ADMIN_PASSWORD=password

# Test User Credentials (optional)
E2E_USER_EMAIL=user@test.com
E2E_USER_PASSWORD=password

# Browser Settings
E2E_HEADLESS=true
E2E_SLOWMO=0
E2E_TIMEOUT=30000

# Screenshot and Video Settings
E2E_SCREENSHOT=only-on-failure
E2E_VIDEO=retain-on-failure
E2E_TRACE=on-first-retry

# Laravel Configuration
LARAVEL_ENV=testing
LARAVEL_DEBUG=true
2 changes: 2 additions & 0 deletions catatan_rilis.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ Di rilis ini, versi 2508.0.0 berisi penambahan dan perbaikan yang diminta penggu
1. [#784](https://github.com/OpenSID/OpenKab/issues/784) Penambahan fungsi export excel pada halaman data statistik pada OpenKab.
2. [#789](https://github.com/OpenSID/OpenKab/issues/789) Penambahan fungsi export data ke excel.
3. [#800](https://github.com/OpenSID/OpenKab/issues/800) Penambahan fungsi untuk penyebutan kelurahan / desa & Kabupaten / Kota.
4. [#777](https://github.com/OpenSID/OpenKab/issues/777) Penambahan statistik pekerja migran penduduk.

#### Perbaikan BUG



#### Perubahan Teknis

1. [#781](https://github.com/OpenSID/OpenKab/issues/781) Penambahan e2e testing untuk OpenKab.
1,754 changes: 1,228 additions & 526 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@
"scripts": {
"dev": "vite",
"build": "vite build",
"build-web": "vite --config vite.config-web.js build && node replace"
"build-web": "vite --config vite.config-web.js build && node replace",
"test:e2e": "playwright test",
"test:e2e:headed": "playwright test --headed",
"test:e2e:ui": "playwright test --ui",
"test:e2e:debug": "playwright test --debug",
"test:e2e:report": "playwright show-report"
},
"devDependencies": {
"@playwright/test": "^1.54.1",
"admin-lte": "3.2.0",
"axios": "^1.1.2",
"jquery": "^3.6",
Expand Down
56 changes: 56 additions & 0 deletions playwright.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const { defineConfig, devices } = require('@playwright/test');
const configLoader = require('./tests/config-loader');

module.exports = defineConfig({
testDir: './tests/e2e',
fullyParallel: false,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 1,
workers: process.env.CI ? 1 : 2,

// Global setup untuk authentication
globalSetup: require.resolve('./tests/global-setup'), reporter: [
['html'],
['json', { outputFile: 'test-results/results.json' }],
['list']
],

use: {
baseURL: configLoader.get('app.baseURL'),
trace: configLoader.get('media.trace'),
screenshot: configLoader.get('media.screenshot'),
video: configLoader.get('media.video'),
actionTimeout: configLoader.get('app.timeout'),
navigationTimeout: configLoader.get('app.timeout'),

// Additional settings for better reliability
ignoreHTTPSErrors: true,
bypassCSP: true,

// Set viewport
viewport: { width: 1280, height: 720 },

// User agent
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
},

projects: [
// Project untuk test yang memerlukan authentication
{
name: 'authenticated',
use: {
...devices['Desktop Chrome'],
// Use saved authentication state
storageState: './test-results/storage-state/auth.json'
},
//testIgnore: ['**/login.spec.js', '**/homepage.spec.js', '**/homepage-link.spec.js'] // Skip login tests untuk authenticated project
},
],

// webServer: {
// command: `${configLoader.get('app.phpVersion')} artisan serve`,
// url: configLoader.get('app.baseURL'),
// reuseExistingServer: !process.env.CI,
// timeout: 120 * 1000,
// },
});
7 changes: 7 additions & 0 deletions resources/views/penduduk/filter_form.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,13 @@ class="form-control select2-filter"data-option='{!! json_encode(App\Models\Enums
value="{{ $filters['suku'] ?? '' }}">
</div>
</div>
<div class="col-sm">
<div class="form-group">
<label>Pekerja Migran</label>
<input id="pekerja_migran" class="form-control" placeholder="Pekerja Migran" type="text"
value="{{ $filters['pekerja_migran'] ?? '' }}">
</div>
</div>
</div>
<div class="row">
<div class="col-sm">
Expand Down
1 change: 1 addition & 0 deletions resources/views/penduduk/index.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@
"filter[id_asuransi]": $('#id_asuransi').val(),
"filter[hamil]": $('#hamil').val(),
"filter[suku]": $('#suku').val(),
"filter[pekerja_migran]": $('#pekerja_migran').val(),
"filter[status_covid]": $('#status_covid').val(),
"filter[status_rekam]": $('#status_rekam').val(),
"filter[pendidikan_kk_id]": $('#pendidikan_kk_id').val(),
Expand Down
157 changes: 157 additions & 0 deletions tests/config-loader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
const fs = require('fs');
const path = require('path');

class E2EConfigLoader {
constructor() {
this.config = this.loadConfiguration();
}

loadConfiguration() {
let config = this.getDefaultConfig();

// Load environment file
try {
const envPath = path.join(__dirname, '..', '.env.e2e');
if (fs.existsSync(envPath)) {
this.loadEnvFile(envPath);
}
} catch (error) {
console.warn('Could not load .env.e2e:', error.message);
}

// Override with environment variables
config = this.overrideWithEnv(config);

return config;
}

getDefaultConfig() {
return {
auth: {
email: 'test@test.com', // Test user yang dibuat
password: 'password' // Password yang diketahui
},
app: {
baseURL: 'http://localhost:8000',
timeout: 30000,
phpVersion: 'php' // Default PHP executable
},
database: {
connection: 'mysql',
host: 'localhost',
port: 3306,
database: 'opendesa_test',
username: 'root',
password: 'secret'
},
testData: {
},
browser: {
headless: true,
slowMo: 0
},
media: {
screenshot: 'only-on-failure',
video: 'retain-on-failure',
trace: 'on-first-retry'
}
};
}

loadEnvFile(filePath) {
const envContent = fs.readFileSync(filePath, 'utf8');
envContent.split('\n').forEach(line => {
const [key, ...valueParts] = line.split('=');
if (key && valueParts.length > 0) {
const value = valueParts.join('=').trim();
process.env[key.trim()] = value;
}
});
}

overrideWithEnv(config) {
// Auth overrides
if (process.env.E2E_ADMIN_EMAIL) {
config.auth.email = process.env.E2E_ADMIN_EMAIL;
}
if (process.env.E2E_ADMIN_PASSWORD) {
config.auth.password = process.env.E2E_ADMIN_PASSWORD;
}

// App overrides
if (process.env.E2E_APP_URL) {
config.app.baseURL = process.env.E2E_APP_URL;
}
if (process.env.E2E_TIMEOUT) {
config.app.timeout = parseInt(process.env.E2E_TIMEOUT);
}
if (process.env.E2E_PHP_VERSION) {
config.app.phpVersion = process.env.E2E_PHP_VERSION;
}

// Database overrides
if (process.env.E2E_DB_CONNECTION) {
config.database.connection = process.env.E2E_DB_CONNECTION;
}
if (process.env.E2E_DB_HOST) {
config.database.host = process.env.E2E_DB_HOST;
}
if (process.env.E2E_DB_PORT) {
config.database.port = parseInt(process.env.E2E_DB_PORT);
}
if (process.env.E2E_DB_DATABASE) {
config.database.database = process.env.E2E_DB_DATABASE;
}
if (process.env.E2E_DB_USERNAME) {
config.database.username = process.env.E2E_DB_USERNAME;
}
if (process.env.E2E_DB_PASSWORD) {
config.database.password = process.env.E2E_DB_PASSWORD;
}

// Browser overrides
if (process.env.E2E_HEADLESS) {
config.browser.headless = process.env.E2E_HEADLESS === 'true';
}
if (process.env.E2E_SLOWMO) {
config.browser.slowMo = parseInt(process.env.E2E_SLOWMO);
}

// Media overrides
if (process.env.E2E_SCREENSHOT) {
config.media.screenshot = process.env.E2E_SCREENSHOT;
}
if (process.env.E2E_VIDEO) {
config.media.video = process.env.E2E_VIDEO;
}
if (process.env.E2E_TRACE) {
config.media.trace = process.env.E2E_TRACE;
}

return config;
}

mergeConfig(target, source) {
const merged = { ...target };

for (const key in source) {
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
merged[key] = this.mergeConfig(merged[key] || {}, source[key]);
} else {
merged[key] = source[key];
}
}

return merged;
}

get(path = '') {
if (!path) return this.config;

return path.split('.').reduce((obj, key) => obj?.[key], this.config);
}
}

// Singleton instance
const configLoader = new E2EConfigLoader();
module.exports = configLoader;
Loading