mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-23 03:33:53 +00:00
feat: Add automated screenshot generation script | Implements Playwright script (scripts/take-screenshots.js) to capture UI views. Adds npm run screenshot command. Includes playwright as a dev dependency. Updates README.md to use generated screenshots. (#73)
This commit is contained in:
@@ -6,7 +6,26 @@
|
||||
|
||||
A lightweight monitoring application for Proxmox VE that displays real-time status for VMs and containers via a simple web interface.
|
||||
|
||||

|
||||

|
||||
|
||||
### 📸 Screenshots
|
||||
|
||||
<details>
|
||||
<summary><strong>Click to view other UI tabs</strong></summary>
|
||||
|
||||
**Nodes Tab:**
|
||||

|
||||
|
||||
**Main Tab (Filtered for VMs):**
|
||||

|
||||
|
||||
**PBS Tab:**
|
||||

|
||||
|
||||
**Backups Tab:**
|
||||

|
||||
|
||||
</details>
|
||||
|
||||
[](https://ko-fi.com/rcourtman)
|
||||
|
||||
@@ -197,165 +216,4 @@ Using Docker Compose is the recommended way for most users.
|
||||
2. **Run:** In the project root directory, run:
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
This downloads the `rcourtman/pulse:latest` image and starts the container in the background.
|
||||
3. **Access:** Open `http://<your-host-ip>:7655` (or the host port mapped in `docker-compose.yml`).
|
||||
|
||||
**Stopping:**
|
||||
```bash
|
||||
docker compose down
|
||||
```
|
||||
|
||||
*Note: Restart the container (`docker compose down && docker compose up -d`) if you change `.env` after starting.*
|
||||
|
||||
<details>
|
||||
<summary><strong>Alternative: Inline Variables in `docker-compose.yml` (Click to Expand)</strong></summary>
|
||||
|
||||
You can define environment variables directly in `docker-compose.yml` instead of using `.env`. **Replace placeholder values** before running `docker compose up -d`.
|
||||
|
||||
```yaml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
pulse:
|
||||
image: rcourtman/pulse:latest
|
||||
container_name: pulse_monitor
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- "7655:7655" # Map container port 7655 to host port 7655
|
||||
environment:
|
||||
# --- Required Proxmox Connection Details ---
|
||||
PROXMOX_HOST: "https://your-proxmox-ip-or-hostname:8006"
|
||||
PROXMOX_TOKEN_ID: "your-user@pam!your-token-name"
|
||||
PROXMOX_TOKEN_SECRET: "your-api-token-secret-uuid"
|
||||
|
||||
# --- Optional Settings ---
|
||||
PROXMOX_ALLOW_SELF_SIGNED_CERTS: "false"
|
||||
# PBS_HOST: "https://your-pbs-ip:8007"
|
||||
# PBS_TOKEN_ID: "your-pbs-user@pbs!token"
|
||||
# PBS_TOKEN_SECRET: "your-pbs-secret"
|
||||
# PBS_NODE_NAME: "your-pbs-hostname"
|
||||
|
||||
# Optional: Mount a local directory for potential future config needs
|
||||
# volumes:
|
||||
# - ./pulse_config:/config
|
||||
|
||||
networks:
|
||||
default:
|
||||
driver: bridge
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Running with LXC Installation Script
|
||||
|
||||
An installation script is available for setting up Pulse inside an **existing** Debian/Ubuntu-based Proxmox VE LXC container.
|
||||
|
||||
**Prerequisites:**
|
||||
- A running Proxmox VE host.
|
||||
- An existing Debian or Ubuntu LXC container with network access to Proxmox.
|
||||
- *Tip: Use [Community Scripts](https://community-scripts.github.io/ProxmoxVE/scripts?id=debian) to easily create one: `bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/debian.sh)"`*
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. **Access LXC Console:** Log in to your LXC container (usually as `root`).
|
||||
2. **Download and Run Script:**
|
||||
```bash
|
||||
# Ensure you are in a suitable directory, like /root or /tmp
|
||||
curl -sLO https://raw.githubusercontent.com/rcourtman/Pulse/main/scripts/install-pulse.sh
|
||||
chmod +x install-pulse.sh
|
||||
./install-pulse.sh
|
||||
```
|
||||
3. **Follow Prompts:** The script guides you through:
|
||||
* Installing dependencies (`git`, `curl`, `nodejs`, `npm`, `sudo`).
|
||||
* Entering your Proxmox Host URL, API Token ID, Secret, and self-signed cert preference.
|
||||
* (Optional) Entering PBS connection details if desired.
|
||||
* Setting up Pulse as a `systemd` service (`pulse-monitor.service`).
|
||||
* Optionally enabling automatic updates via cron.
|
||||
4. **Access Pulse:** The script will display the URL (e.g., `http://<LXC-IP-ADDRESS>:7655`).
|
||||
|
||||
<details>
|
||||
<summary><strong>Updating and Managing the LXC Installation (Click to Expand)</strong></summary>
|
||||
|
||||
**Updating Pulse:**
|
||||
|
||||
Re-run the script from the directory where you downloaded it:
|
||||
```bash
|
||||
./install-pulse.sh
|
||||
```
|
||||
Or run non-interactively (e.g., for cron):
|
||||
```bash
|
||||
./install-pulse.sh --update
|
||||
```
|
||||
|
||||
**Managing the Pulse Service:**
|
||||
|
||||
Use standard `systemctl` commands:
|
||||
* Check Status: `sudo systemctl status pulse-monitor.service`
|
||||
* Stop Service: `sudo systemctl stop pulse-monitor.service`
|
||||
* Start Service: `sudo systemctl start pulse-monitor.service`
|
||||
* View Logs: `sudo journalctl -u pulse-monitor.service -f`
|
||||
* Enable/Disable on Boot: `sudo systemctl enable/disable pulse-monitor.service`
|
||||
|
||||
**Automatic Updates:**
|
||||
If enabled via the script, a cron job runs `./install-pulse.sh --update` Daily/Weekly/Monthly. Logs are in `/var/log/pulse_update.log`. Manage with `sudo crontab -l -u root` or `sudo crontab -e -u root`.
|
||||
|
||||
</details>
|
||||
|
||||
### Running the Application (Node.js - Development)
|
||||
|
||||
For development purposes or running directly from source, see the **[DEVELOPMENT.md](DEVELOPMENT.md)** guide. This involves cloning the repository, installing dependencies using `npm install` in both the root and `server` directories, and running `npm run dev` or `npm run start`.
|
||||
|
||||
## ✨ Features
|
||||
|
||||
- Lightweight monitoring for Proxmox VE nodes, VMs, and Containers.
|
||||
- Real-time status updates via WebSockets.
|
||||
- Simple, responsive web interface.
|
||||
- Efficient polling: Stops API polling when no clients are connected.
|
||||
- Docker support.
|
||||
- Multi-environment PVE monitoring support.
|
||||
- Proxmox Backup Server (PBS) monitoring support.
|
||||
- LXC installation script.
|
||||
|
||||
## 💻 System Requirements
|
||||
|
||||
- **Node.js:** Version 18.x or later (if building/running from source).
|
||||
- **NPM:** Compatible version with Node.js.
|
||||
- **Docker & Docker Compose:** Latest stable versions (if using container deployment).
|
||||
- **Proxmox VE:** Version 7.x or 8.x recommended.
|
||||
- **Proxmox Backup Server:** Version 2.x or 3.x recommended (if monitored).
|
||||
- **Web Browser:** Modern evergreen browser.
|
||||
|
||||
## 👋 Contributing
|
||||
|
||||
Contributions are welcome! Please read our [Contributing Guidelines](CONTRIBUTING.md).
|
||||
|
||||
## 🔒 Privacy
|
||||
|
||||
* **No Data Collection:** Pulse does not collect or transmit any telemetry or user data externally.
|
||||
* **Local Communication:** Operates entirely between your environment and your Proxmox/PBS APIs.
|
||||
* **Credential Handling:** Credentials are used only for API authentication and are not logged or sent elsewhere.
|
||||
|
||||
## 📜 License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file.
|
||||
|
||||
## ™️ Trademark Notice
|
||||
|
||||
Proxmox® and Proxmox VE® are registered trademarks of Proxmox Server Solutions GmbH. This project is not affiliated with or endorsed by Proxmox Server Solutions GmbH.
|
||||
|
||||
## ❤️ Support
|
||||
|
||||
File issues on the [GitHub repository](https://github.com/rcourtman/Pulse/issues).
|
||||
|
||||
If you find Pulse useful, consider supporting its development:
|
||||
[](https://ko-fi.com/rcourtman)
|
||||
|
||||
## ❓ Troubleshooting
|
||||
|
||||
Common connection issues:
|
||||
|
||||
* **Pulse Application Logs:** Check container logs (`docker logs pulse_monitor`) or service logs (`sudo journalctl -u pulse-monitor.service -f`) for errors (401 Unauthorized, 403 Forbidden, connection refused, timeout).
|
||||
* **`.env` Configuration:** Verify `PROXMOX_HOST`, `PROXMOX_TOKEN_ID`, `PROXMOX_TOKEN_SECRET`, and `PROXMOX_ALLOW_SELF_SIGNED_CERTS`. For PBS, also check `PBS_HOST`, `PBS_TOKEN_ID`, `PBS_TOKEN_SECRET`, `PBS_ALLOW_SELF_SIGNED_CERTS`, and especially **`PBS_NODE_NAME`**. Ensure no placeholder values remain.
|
||||
* **Network Connectivity:** Can the machine running Pulse reach the PVE/PBS hostnames/IPs and ports (usually 8006 for PVE, 8007 for PBS)? Check firewalls.
|
||||
* **API Token Permissions:** Ensure the correct roles (`PVEAuditor` for PVE, `Audit` for PBS) are assigned at the root path (`/`) with `Propagate` enabled in the respective UIs.
|
||||
```
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 519 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 145 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 519 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 424 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 576 KiB |
Generated
+48
@@ -18,6 +18,7 @@
|
||||
"conventional-changelog-cli": "^5.0.0",
|
||||
"dotenv": "^16.5.0",
|
||||
"jest": "^29.7.0",
|
||||
"playwright": "^1.52.0",
|
||||
"postcss": "^8.5.3",
|
||||
"tailwindcss": "^3.4.4"
|
||||
}
|
||||
@@ -4680,6 +4681,53 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.52.0.tgz",
|
||||
"integrity": "sha512-JAwMNMBlxJ2oD1kce4KPtMkDeKGHQstdpFPcPH3maElAXon/QZeTvtsfXmTMRyO9TslfoYOXkSsvao2nE1ilTw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.52.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.52.0.tgz",
|
||||
"integrity": "sha512-l2osTgLXSMeuLZOML9qYODUQoPPnUsKsb5/P6LJ2e6uPKXUdPK5WYhN4z03G+YNbWmGDY4YENauNu4ZKczreHg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright/node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.5.3",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
|
||||
|
||||
+3
-1
@@ -9,7 +9,8 @@
|
||||
"dev:css": "tailwindcss -c ./src/tailwind.config.js -i ./src/index.css -o ./src/public/output.css --watch",
|
||||
"build:css": "tailwindcss -c ./src/tailwind.config.js -i ./src/index.css -o ./src/public/output.css",
|
||||
"dev": "concurrently \"npm:dev:server\" \"npm:dev:css\"",
|
||||
"test": "jest"
|
||||
"test": "jest",
|
||||
"screenshot": "node scripts/take-screenshots.js"
|
||||
},
|
||||
"keywords": [
|
||||
"proxmox",
|
||||
@@ -31,6 +32,7 @@
|
||||
"conventional-changelog-cli": "^5.0.0",
|
||||
"dotenv": "^16.5.0",
|
||||
"jest": "^29.7.0",
|
||||
"playwright": "^1.52.0",
|
||||
"postcss": "^8.5.3",
|
||||
"tailwindcss": "^3.4.4"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
const { chromium } = require('playwright');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
|
||||
// --- Configuration ---
|
||||
const BASE_URL = process.env.PULSE_URL || 'http://localhost:7655'; // Allow overriding via env var
|
||||
const OUTPUT_DIR = path.resolve(__dirname, '../docs/images');
|
||||
const VIEWPORT = { width: 1440, height: 900 }; // Match common M1 Air scaled resolution (16:10)
|
||||
const WAIT_OPTIONS = { waitUntil: 'networkidle', timeout: 15000 }; // Increased timeout, networkidle
|
||||
const OVERLAY_SELECTOR = '#loading-overlay';
|
||||
|
||||
// Define the sections to capture
|
||||
// Placeholder selectors/navigation logic will need refinement
|
||||
const sections = [
|
||||
// Dashboard: Wait for guest row, capture FULL page
|
||||
{ name: '01-dashboard',
|
||||
fullPage: true,
|
||||
action: async (page) => {
|
||||
console.log(' Action: Waiting for dashboard content to load (checking for removal of loading text)...');
|
||||
// Wait for the first row that DOES NOT contain the loading text TD
|
||||
await page.locator('#main-table tbody tr:not(:has(td:text("Loading data...")))').first().waitFor({ state: 'visible', timeout: 30000 });
|
||||
console.log(' Action: Dashboard content loaded (loading text gone).');
|
||||
}
|
||||
},
|
||||
|
||||
// Node View: Click tab, wait for table rows, capture nodes tab content
|
||||
{ name: '02-node-view',
|
||||
screenshotTarget: '#nodes',
|
||||
action: async (page) => {
|
||||
console.log(' Action: Clicking Nodes tab');
|
||||
await page.locator('[data-tab="nodes"]').click();
|
||||
console.log(' Action: Waiting for nodes table to be visible');
|
||||
await page.locator('#nodes #nodes-table').waitFor({ state: 'visible', timeout: 10000 });
|
||||
console.log(' Action: Nodes table visible');
|
||||
// Optional: wait for rows as well, though table visibility might be enough
|
||||
// await page.locator('#nodes-table-body tr').first().waitFor({ state: 'visible', timeout: 10000 });
|
||||
}
|
||||
},
|
||||
|
||||
// VM View: Click main tab, click VM filter, wait, capture main content
|
||||
{ name: '03-vm-container-view', // Renaming slightly as it shows filtered list
|
||||
screenshotTarget: '#main',
|
||||
action: async (page) => {
|
||||
console.log(' Action: Clicking Main tab (if not already active)');
|
||||
// Ensure main tab is active first
|
||||
const mainTabIsActive = await page.locator('[data-tab="main"].active').isVisible();
|
||||
if (!mainTabIsActive) {
|
||||
await page.locator('[data-tab="main"]').click();
|
||||
await page.waitForLoadState('networkidle', { timeout: 5000 });
|
||||
}
|
||||
|
||||
// Wait for the VM filter button's LABEL to be visible before clicking
|
||||
console.log(' Action: Waiting for VM filter label to be visible');
|
||||
const vmFilterLabel = page.locator('label[for="filter-vm"]'); // Target the label now
|
||||
await vmFilterLabel.waitFor({ state: 'visible', timeout: 15000 });
|
||||
console.log(' Action: VM filter label visible');
|
||||
|
||||
console.log(' Action: Clicking VM filter label');
|
||||
await vmFilterLabel.click(); // Click the label
|
||||
await page.waitForTimeout(1000);
|
||||
console.log(' Action: VM filter applied');
|
||||
}
|
||||
},
|
||||
|
||||
// PBS View: Click tab, wait for PBS container content, capture PBS tab content
|
||||
{ name: '04-pbs-view',
|
||||
screenshotTarget: '#pbs',
|
||||
action: async (page) => {
|
||||
console.log(' Action: Clicking PBS tab');
|
||||
await page.locator('[data-tab="pbs"]').click();
|
||||
console.log(' Action: Waiting for PBS container to be visible');
|
||||
// Wait for the main container within the PBS tab to be visible
|
||||
await page.locator('#pbs #pbs-instances-container').waitFor({ state: 'visible', timeout: 10000 });
|
||||
console.log(' Action: PBS container visible');
|
||||
// Optional: wait for actual content rows if needed
|
||||
// await page.locator('#pbs-instances-container > *').first().waitFor({ state: 'visible', timeout: 10000 });
|
||||
}
|
||||
},
|
||||
|
||||
// Backups View: Click tab, wait for table content, capture backups tab content
|
||||
{ name: '05-backups-view', // Added new section
|
||||
screenshotTarget: '#backups',
|
||||
action: async (page) => {
|
||||
console.log(' Action: Clicking Backups tab');
|
||||
await page.locator('[data-tab="backups"]').click();
|
||||
console.log(' Action: Waiting for backups table row to be visible');
|
||||
// Wait for the first row in the backups table body
|
||||
await page.locator('#backups-overview-tbody tr').first().waitFor({ state: 'visible', timeout: 15000 });
|
||||
console.log(' Action: Backups table row visible');
|
||||
}
|
||||
},
|
||||
|
||||
// { name: '06-task-view', url: '/#tasks', screenshotTarget: '#task-list-element', action: async (page) => { /* Navigate to task view if separate */ } }, // Uncomment and adjust if needed
|
||||
];
|
||||
|
||||
async function takeScreenshots() {
|
||||
console.log(`Starting screenshot capture for ${BASE_URL}...`);
|
||||
console.log(`Outputting to: ${OUTPUT_DIR}`);
|
||||
|
||||
// --- Clean up existing PNG files ---
|
||||
if (fs.existsSync(OUTPUT_DIR)) {
|
||||
console.log(`Cleaning up existing *.png files in ${OUTPUT_DIR}...`);
|
||||
const files = fs.readdirSync(OUTPUT_DIR);
|
||||
let deletedCount = 0;
|
||||
files.forEach(file => {
|
||||
if (path.extname(file).toLowerCase() === '.png') {
|
||||
const filePath = path.join(OUTPUT_DIR, file);
|
||||
try {
|
||||
fs.unlinkSync(filePath);
|
||||
// console.log(` Deleted: ${file}`); // Optional: more verbose logging
|
||||
deletedCount++;
|
||||
} catch (err) {
|
||||
console.error(` Error deleting file ${file}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
console.log(`Cleanup finished. Deleted ${deletedCount} PNG file(s).`);
|
||||
} else {
|
||||
console.log('Output directory does not exist, no cleanup needed.');
|
||||
}
|
||||
// --- End Cleanup ---
|
||||
|
||||
// Ensure output directory exists (might have been deleted if empty or just created)
|
||||
if (!fs.existsSync(OUTPUT_DIR)) {
|
||||
console.log(`Creating directory: ${OUTPUT_DIR}`);
|
||||
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
let browser;
|
||||
try {
|
||||
browser = await chromium.launch(); // Or firefox, webkit
|
||||
const context = await browser.newContext({
|
||||
viewport: VIEWPORT,
|
||||
ignoreHTTPSErrors: true, // Helpful if using self-signed certs for Pulse dev
|
||||
deviceScaleFactor: 2 // Render at 2x detail for higher quality screenshots
|
||||
});
|
||||
const page = await context.newPage();
|
||||
|
||||
console.log('Navigating to base URL and waiting for initial load...');
|
||||
await page.goto(BASE_URL, WAIT_OPTIONS);
|
||||
|
||||
// Wait for the loading overlay to disappear before starting captures
|
||||
console.log(`Waiting for overlay (${OVERLAY_SELECTOR}) to disappear...`);
|
||||
await page.locator(OVERLAY_SELECTOR).waitFor({ state: 'hidden', timeout: 20000 }); // Increased timeout
|
||||
console.log('Overlay hidden.');
|
||||
|
||||
// --- Ensure Dark Mode ---
|
||||
console.log('Ensuring dark mode is active...');
|
||||
const isDarkMode = await page.evaluate(() => document.documentElement.classList.contains('dark'));
|
||||
if (!isDarkMode) {
|
||||
console.log(' Dark mode not active, clicking theme toggle button...');
|
||||
const themeButton = page.locator('#theme-toggle-button');
|
||||
await themeButton.waitFor({ state: 'visible', timeout: 5000 });
|
||||
await themeButton.click();
|
||||
await page.waitForTimeout(500); // Wait for theme transition
|
||||
console.log(' Dark mode toggled.');
|
||||
} else {
|
||||
console.log(' Dark mode already active.');
|
||||
}
|
||||
// --- End Ensure Dark Mode ---
|
||||
|
||||
console.log('Starting section captures.');
|
||||
|
||||
for (const section of sections) {
|
||||
const screenshotPath = path.join(OUTPUT_DIR, `${section.name}.png`);
|
||||
console.log(`Capturing section: ${section.name}...`);
|
||||
|
||||
try {
|
||||
// Perform specific actions if needed (clicks, etc.)
|
||||
if (section.action) {
|
||||
console.log(` Performing action for ${section.name}...`);
|
||||
await section.action(page);
|
||||
// Wait after action for UI to settle - using networkidle should be sufficient
|
||||
await page.waitForLoadState('networkidle', { timeout: 10000 });
|
||||
console.log(' Action completed and network idle.');
|
||||
} else {
|
||||
// Ensure we are on the main tab for sections without specific actions
|
||||
// (Applies mainly if dashboard wasn't the very first step)
|
||||
const mainTabIsActive = await page.locator('[data-tab="main"].active').isVisible();
|
||||
if (!mainTabIsActive) {
|
||||
await page.locator('[data-tab="main"]').click();
|
||||
await page.waitForLoadState('networkidle', { timeout: 5000 });
|
||||
}
|
||||
}
|
||||
|
||||
// Take the screenshot
|
||||
let elementToCapture;
|
||||
let captureFullPage = section.fullPage || false; // Get flag, default false
|
||||
|
||||
console.log(` Locating screenshot target: ${section.screenshotTarget || 'page (fullPage: '+captureFullPage+')'}`);
|
||||
if (section.screenshotTarget) { // Check if a specific target is defined
|
||||
elementToCapture = page.locator(section.screenshotTarget).first();
|
||||
console.log(' Waiting for screenshot target element to be visible...');
|
||||
await elementToCapture.waitFor({ state: 'visible', timeout: 10000 });
|
||||
console.log(' Target element visible.');
|
||||
captureFullPage = false; // Never capture full page when targeting a specific element
|
||||
} else {
|
||||
elementToCapture = page; // Use the page itself for viewport/fullpage screenshots
|
||||
}
|
||||
|
||||
console.log(` Saving screenshot to: ${screenshotPath}`);
|
||||
if (elementToCapture === page) {
|
||||
// Capture viewport or full page based on the flag
|
||||
console.log(` Capturing ${captureFullPage ? 'full page' : 'viewport'}`);
|
||||
await page.screenshot({ path: screenshotPath, fullPage: captureFullPage });
|
||||
} else {
|
||||
// Capture specific element
|
||||
console.log(' Capturing specific element');
|
||||
await elementToCapture.screenshot({ path: screenshotPath });
|
||||
}
|
||||
|
||||
console.log(` Successfully captured ${section.name}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error(` Failed to capture section ${section.name}: ${error.message}`);
|
||||
// Optionally, decide if you want to continue or stop on error
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Error during screenshot process: ${error}`);
|
||||
process.exitCode = 1; // Indicate failure
|
||||
} finally {
|
||||
if (browser) {
|
||||
await browser.close();
|
||||
console.log('Browser closed.');
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Screenshot capture finished.');
|
||||
}
|
||||
|
||||
takeScreenshots();
|
||||
Reference in New Issue
Block a user