I'm Samuel FajreldinesI am a specialist in the entire JavaScript and TypeScript ecosystem (including Node.js, React, Angular and Vue.js) I am expert in AI and in creating AI integrated solutions I am expert in DevOps and Serverless Architecture (AWS, Google Cloud and Azure) I am expert in PHP and its frameworks (such as Codeigniter and Laravel). |
Samuel FajreldinesI am a specialist in the entire JavaScript and TypeScript ecosystem. I am expert in AI and in creating AI integrated solutions. I am expert in DevOps and Serverless Architecture I am expert in PHP and its frameworks.
|
End-to-end (E2E) testing is a powerful strategy for verifying the integrity of an application’s entire workflow—from user interactions in the front end to business logic in the backend—ensuring that all layers function together seamlessly. E2E tests replicate real user scenarios, making them essential in complex environments that span multiple services, data layers, and user interfaces. In today’s competitive software landscape, having a stable and confident release pipeline is crucial, and end-to-end tests often serve as the final guardrail before pushing changes into production.
Modern web development leverages frameworks such as React, Angular, and Vue.js on the front end, while Node.js or PHP frameworks like Laravel and CodeIgniter may handle backend logic, often deployed on cloud services like AWS, Google Cloud, or Azure. Automated E2E tests help identify regressions, confirm multi-service interactions, and ensure high-quality user experiences. This post explores E2E testing fundamentals and demonstrates how to implement a robust test suite with Playwright—an increasingly popular open-source testing framework backed by Microsoft.
When developers build applications, they often write unit and integration tests to validate isolated functions or modules. While these tests promote code quality, they do not always reflect how real users operate the system. End-to-end testing, by contrast, simulates real-world usage:
• It validates that the application’s UI (whether built with React, Angular, or Vue.js) displays content correctly, processes user inputs, and returns the right information.
• It ensures back-end services (in Node.js, PHP, or any other language) respond as expected, integrate with databases, and handle requests properly.
• It reveals hidden issues that can surface only when the entire application chain is tested under conditions resembling production.
In essence, E2E tests bridge the gap between individual component validation and the real-life user journey. They prevent critical bugs from reaching production and empower teams by offering more confidence in the codebase before deployment.
Playwright is an E2E testing framework developed by Microsoft. It offers several benefits that make it well-suited for modern JavaScript and TypeScript projects:
When combined with typical front-end frameworks (React, Angular, Vue.js) and various serverless or containerized deployments, Playwright serves as an all-in-one solution for consistent cross-browser test coverage.
Getting started with Playwright can be done quickly on any JavaScript or TypeScript project:
Create a Project Directory
Create a fresh folder, or use an existing Node.js or TypeScript project where you’d like to place your tests. For instance:
mkdir playwright-e2e-tests
cd playwright-e2e-tests
Initialize the Project
You can initialize a new Node.js project if one does not exist yet:
npm init -y
Install Playwright
Install the core package alongside its test runner and the necessary browser binaries:
npm install --save-dev @playwright/test
npx playwright install
The above command downloads browser executables (Chrome, Firefox, and WebKit) for cross-browser testing.
Optional: TypeScript Configuration
If you haven’t already configured TypeScript, initialize it:
npx tsc --init
Then update your tsconfig.json as needed. Common settings might include "strict": true, "module": "commonjs", and "target": "es6" or higher.
Project Structure
Your file layout could look like this:
playwright-e2e-tests/
├── tests/
│ └── example.spec.ts
├── package.json
├── tsconfig.json
└── playwright.config.ts
The tests folder is where you keep your test files, which you can split logically based on features, routes, or modules.
An essential test might check if your home page loads and if a particular UI element, like a welcome message, appears:
import { test, expect } from '@playwright/test';
test('homepage has expected title and welcome message', async ({ page }) => {
await page.goto('https://example.com');
// Check the page title
await expect(page).toHaveTitle(/Example Domain/);
// Check if 'Example Domain' text is present
const heading = await page.locator('h1');
await expect(heading).toContainText('Example Domain');
});
In this simple scenario:
• The test opens a browser instance in a new context, navigates to “example.com,” and waits for the page to load.
• It verifies that the page title matches the expected pattern.
• It checks whether the main heading contains the correct text.
Because Playwright automatically waits for elements to be ready before performing actions, you typically avoid brittle synchronization issues. You can execute this test with:
npx playwright test
To unlock Playwright’s full potential, you can customize your test runner through a configuration file often named playwright.config.ts. For instance:
import { defineConfig } from '@playwright/test';
export default defineConfig({
testDir: 'tests',
timeout: 30000,
expect: {
timeout: 5000,
},
use: {
headless: true,
screenshot: 'only-on-failure',
trace: 'on-first-retry',
},
projects: [
{
name: 'Chromium',
use: { browserName: 'chromium' },
},
{
name: 'Firefox',
use: { browserName: 'firefox' },
},
{
name: 'WebKit',
use: { browserName: 'webkit' },
},
],
});
Key points in this configuration:
• testDir defines where your test files reside.
• timeout sets a maximum duration for each test.
• The use property configures default behaviors like running tests headless and capturing screenshots on failures.
• The projects array defines multiple browser types, allowing you to run the same tests in parallel across Chromium, Firefox, and WebKit.
With parallel cross-browser tests, your E2E suite can catch browser-specific rendering or API inconsistencies quickly—an invaluable asset when your application caters to diverse user segments.
Dynamic single-page applications (SPAs) often require user authentication, making E2E tests more complex:
Example of preserving auth state:
// global-setup.ts
import { test as setup, expect } from '@playwright/test';
setup('authenticate once', async ({ page }) => {
await page.goto('https://my-app.com/login');
await page.fill('#username', 'testuser');
await page.fill('#password', 'testpassword');
await page.click('button[type="submit"]');
// Wait for a known element after login
await expect(page.locator('#dashboard')).toBeVisible();
// Save state
await page.context().storageState({ path: 'authState.json' });
});
Then inside your playwright.config.ts
, you can define:
import { defineConfig } from '@playwright/test';
export default defineConfig({
globalSetup: require.resolve('./global-setup'),
use: {
storageState: 'authState.json',
},
// ... other configurations
});
This approach allows subsequent tests to begin with a logged-in session, improving speed and reliability.
When a test fails, it’s vital to diagnose issues quickly and precisely:
• Screenshots and Videos: Configure Playwright to automatically capture screenshots on failure or record videos of test runs to pinpoint exactly what went wrong.
• Traces: Playwright’s trace feature captures a timeline of actions, logs, and network requests. You can open these traces in a UI to see the step-by-step breakdown of each failing test.
• Headed Mode: For local debugging, you can disable headless mode, watch your tests run in the browser, and inspect the DOM or network calls thoroughly.
test('debugging example', async ({ page }) => {
await page.goto('https://example.com');
// Intentionally failing step
await expect(page.locator('.non-existent')).toBeVisible();
});
Should this test fail, you can review the screenshot, trace, or watch it in headed mode:
npx playwright test --headed --trace=on
Continuous Integration (CI) pipelines that automatically install dependencies, run tests, and report results are essential for modern software teams. Popular platforms like GitHub Actions, GitLab CI, or Jenkins provide straightforward ways to integrate Playwright:
An example GitHub Actions workflow:
name: Playwright E2E Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Node
uses: actions/setup-node@v2
with:
node-version: '16'
- name: Install dependencies
run: npm install
- name: Install browsers
run: npx playwright install
- name: Run tests
run: npx playwright test
By syncing your E2E suite with a CI/CD pipeline, every code push automatically triggers tests, providing immediate feedback to teams on the quality and stability of changes.
By following these guidelines, you maintain a high-impact test suite that’s both reliable and maintainable—a cornerstone of professional software delivery.
Beyond simple page checks, teams can leverage Playwright for advanced test scenarios:
• Responsive Layout Testing: Emulate mobile devices, check for breakpoints, and validate consistent user experiences across various screen sizes.
• Payment and External Integrations: Mock or stub third-party APIs, like payment gateways or email services, to avoid triggering real transactions during tests.
• Visual Regression Testing: Incorporate visual snapshots to catch unintended styling changes or layout shifts. This is especially useful in design-centric applications built with React or Angular.
• API and Component Testing: Though primarily known for E2E, Playwright can also complement lower-level test strategies. You can reuse logic or intercept network calls to handle partial end-to-end flows.
Developers often ship new features multiple times a day or week. Without end-to-end verification, it’s easy for tiny oversights to accumulate into bigger production issues. A well-rounded E2E approach powered by Playwright significantly boosts confidence in the release process. Teams can:
• Automate Regression Checks: Avoid shipping a feature that inadvertently breaks existing functionality.
• Enhance Developer Velocity: Developers iterate quickly when they trust the automated tests to catch regressions.
• Share Clear Reports: Show test coverage and success rates to stakeholders, bridging communication between technical and non-technical audiences.
• Combine with Other Tests: E2E tests complement unit and integration tests, forming a comprehensive testing matrix that covers your code from multiple angles.
End-to-end tests ensure that every major user interaction in an application—from logging in, to performing business-critical tasks, to verifying final outputs—works as intended. With modern frameworks like Playwright, implementing these tests has never been more accessible. You gain cross-browser compatibility, parallel test runs, debugging options, and an ecosystem that seamlessly integrates with DevOps pipelines on AWS, Azure, or Google Cloud.
By building a suite of reliable E2E tests, development teams can ship features rapidly, minimize production risks, and deliver an excellent user experience across all browsers and devices. As applications evolve—whether they incorporate advanced JavaScript, TypeScript code, or leverage PHP backends—Playwright remains a robust solution that scales alongside your project. Ultimately, end-to-end testing is not just about catching bugs; it’s about achieving consistent, high-quality releases that keep users satisfied and your engineering processes efficient.
About Me
Since I was a child, I've always wanted to be an inventor. As I grew up, I specialized in information systems, an area which I fell in love with and live around it. I am a full-stack developer and work a lot with devops, i.e., I'm a kind of "jack-of-all-trades" in IT. Wherever there is something cool or new, you'll find me exploring and learning... I am passionate about life, family, and sports. I believe that true happiness can only be achieved by balancing these pillars. I am always looking for new challenges and learning opportunities, and would love to connect with other technology professionals to explore possibilities for collaboration. If you are looking for a dedicated and committed full-stack developer with a passion for excellence, please feel free to contact me. It would be a pleasure to talk with you! |
SecurityScoreCard
Nov. 2023 - Present
New York, United States
Senior Software Engineer
I joined SecurityScorecard, a leading organization with over 400 employees, as a Senior Full Stack Software Engineer. My role spans across developing new systems, maintaining and refactoring legacy solutions, and ensuring they meet the company's high standards of performance, scalability, and reliability.
I work across the entire stack, contributing to both frontend and backend development while also collaborating directly on infrastructure-related tasks, leveraging cloud computing technologies to optimize and scale our systems. This broad scope of responsibilities allows me to ensure seamless integration between user-facing applications and underlying systems architecture.
Additionally, I collaborate closely with diverse teams across the organization, aligning technical implementation with strategic business objectives. Through my work, I aim to deliver innovative and robust solutions that enhance SecurityScorecard's offerings and support its mission to provide world-class cybersecurity insights.
Technologies Used:
Node.js Terraform React Typescript AWS Playwright and Cypress