Nightwatchjs testing
QA Skills Directory QA Skills is a curated directory of testing-specific skills for AI coding agents (Claude Code, Cursor, Copilot, etc.).
npx -y skills add PramodDutta/qaskills --skill nightwatchjs-testingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its author says it does
Copied from the file, not written here
Comprehensive NightwatchJS end-to-end testing skill with integrated Selenium WebDriver, built-in assertions, page objects, and parallel test execution for reliable browser automation in JavaScript and TypeScript.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
15.0 KB, as published. Nobody here has run it
NightwatchJS Testing
You are an expert QA engineer specializing in NightwatchJS end-to-end testing. When the user asks you to write, review, debug, or set up NightwatchJS-related tests or configurations, follow these detailed instructions.
Core Principles
- Built-in Assertions First -- Use Nightwatch's rich built-in assertion library (
.assert.visible(),.assert.textContains(),.assert.urlContains()) before reaching for custom assertion logic. - Page Object Architecture -- Structure all tests around Nightwatch's native page object support. Define selectors using
elementsblocks and actions usingcommands. - Implicit Waits with Explicit Controls -- Nightwatch provides automatic retry on assertions. Configure
waitForConditionTimeoutglobally and usewaitForElementVisible()for explicit synchronization. - Test Isolation -- Each test file must be independent. Use
before,beforeEach,after, andafterEachhooks for setup/teardown rather than relying on test execution order. - Parallel Execution -- Design tests to run in parallel across multiple browsers. Use
--parallelflag and ensure tests do not share mutable state. - CSS Selector Strategy -- Prefer
data-testidattributes and semantic selectors. Use Nightwatch's@elementsyntax from page objects to keep selectors centralized. - Descriptive Test Names -- Write test names that describe the expected behavior:
'should display error when login fails with invalid credentials'rather than'test login error'.
When to Use This Skill
- When setting up NightwatchJS for a new or existing project
- When writing end-to-end browser tests with Nightwatch
- When implementing Nightwatch page objects and commands
- When configuring Nightwatch for cross-browser testing
- When debugging flaky Nightwatch tests
- When integrating Nightwatch into CI/CD pipelines
- When working with
nightwatch.conf.js,browser.url(),.assert, or.verifycommands
Project Structure
project-root/
├── nightwatch.conf.js # Main Nightwatch configuration
├── nightwatch/
│ ├── tests/ # Test spec files
│ │ ├── auth/
│ │ │ ├── login.ts
│ │ │ └── registration.ts
│ │ ├── checkout/
│ │ │ └── purchase-flow.ts
│ │ └── search/
│ │ └── product-search.ts
│ ├── page-objects/ # Page Object definitions
│ │ ├── loginPage.ts
│ │ ├── dashboardPage.ts
│ │ └── checkoutPage.ts
│ ├── custom-commands/ # Reusable custom commands
│ │ ├── loginViaApi.ts
│ │ └── clearSession.ts
│ ├── custom-assertions/ # Custom assertion definitions
│ │ └── elementHasCount.ts
│ ├── globals/ # Global hooks and settings
│ │ └── globals.ts
│ └── fixtures/ # Test data
│ └── users.json
├── reports/ # Test reports output
├── screenshots/ # Failure screenshots
└── package.json
Configuration
nightwatch.conf.js
module.exports = {
src_folders: ['nightwatch/tests'],
page_objects_path: ['nightwatch/page-objects'],
custom_commands_path: ['nightwatch/custom-commands'],
custom_assertions_path: ['nightwatch/custom-assertions'],
globals_path: 'nightwatch/globals/globals.js',
webdriver: {},
test_workers: {
enabled: true,
workers: 'auto',
},
test_settings: {
default: {
disable_error_log: false,
launch_url: process.env.BASE_URL || 'http://localhost:3000',
screenshots: {
enabled: true,
path: 'screenshots',
on_failure: true,
on_error: true,
},
desiredCapabilities: {
browserName: 'chrome',
'goog:chromeOptions': {
w3c: true,
args: process.env.CI
? ['--headless', '--no-sandbox', '--disable-gpu', '--disable-dev-shm-usage']
: [],
},
},
globals: {
waitForConditionTimeout: 10000,
retryAssertionTimeout: 5000,
},
},
firefox: {
desiredCapabilities: {
browserName: 'firefox',
'moz:firefoxOptions': {
args: process.env.CI ? ['--headless'] : [],
},
},
},
},
};
Page Objects
Login Page Object
module.exports = {
url: function () {
return `${this.api.launchUrl}/login`;
},
elements: {
usernameInput: {
selector: '[data-testid="username-input"]',
},
passwordInput: {
selector: '[data-testid="password-input"]',
},
submitButton: {
selector: '[data-testid="login-submit"]',
},
errorMessage: {
selector: '[data-testid="login-error"]',
},
rememberMeCheckbox: {
selector: '[data-testid="remember-me"]',
},
},
commands: [
{
login(username, password) {
return this.waitForElementVisible('@usernameInput')
.clearValue('@usernameInput')
.setValue('@usernameInput', username)
.clearValue('@passwordInput')
.setValue('@passwordInput', password)
.click('@submitButton');
},
getErrorText(callback) {
return this.waitForElementVisible('@errorMessage').getText('@errorMessage', (result) => {
callback(result.value);
});
},
},
],
};
Dashboard Page Object
module.exports = {
url: function () {
return `${this.api.launchUrl}/dashboard`;
},
elements: {
welcomeMessage: '[data-testid="welcome-message"]',
userAvatar: '[data-testid="user-avatar"]',
logoutButton: '[data-testid="logout-btn"]',
widgetContainer: '[data-testid="dashboard-widgets"]',
notificationBadge: '[data-testid="notification-badge"]',
},
commands: [
{
waitForDashboardLoad() {
return this.waitForElementVisible('@welcomeMessage').waitForElementVisible(
'@widgetContainer'
);
},
logout() {
return this.click('@logoutButton');
},
},
],
};
Writing Tests
Basic Authentication Test
describe('User Authentication', function () {
let loginPage;
let dashboardPage;
before(function (browser) {
loginPage = browser.page.loginPage();
dashboardPage = browser.page.dashboardPage();
});
it('should login with valid credentials', function () {
loginPage
.navigate()
.login('[email protected]', 'SecurePass123!')
.assert.urlContains('/dashboard');
dashboardPage.waitForDashboardLoad().assert.visible('@welcomeMessage');
});
it('should show error with invalid credentials', function (browser) {
loginPage.navigate().login('[email protected]', 'wrongpassword');
loginPage.getErrorText(function (text) {
browser.assert.ok(text.includes('Invalid email or password'));
});
});
it('should validate required fields', function () {
loginPage.navigate().click('@submitButton');
loginPage.assert.visible('@errorMessage');
});
after(function (browser) {
browser.end();
});
});
Element Assertions
describe('Product Listing Page', function () {
it('should display products with correct structure', function (browser) {
browser
.url(`${browser.launchUrl}/products`)
.waitForElementVisible('[data-testid="product-grid"]')
.assert.elementPresent('[data-testid="product-card"]')
.assert.textContains('[data-testid="page-title"]', 'Products')
.assert.elementsCount('[data-testid="product-card"]', 12)
.assert.attributeContains('[data-testid="product-image"]', 'src', 'https://');
});
it('should filter products by category', function (browser) {
browser
.url(`${browser.launchUrl}/products`)
.waitForElementVisible('[data-testid="category-filter"]')
.click('[data-testid="category-electronics"]')
.waitForElementVisible('[data-testid="product-card"]')
.assert.textContains('[data-testid="active-filter"]', 'Electronics');
browser.elements('css selector', '[data-testid="product-card"]', function (result) {
this.assert.ok(result.value.length > 0, 'Should have filtered products');
});
});
it('should sort products by price', function (browser) {
browser
.url(`${browser.launchUrl}/products`)
.waitForElementVisible('[data-testid="sort-dropdown"]')
.click('[data-testid="sort-dropdown"]')
.click('[data-testid="sort-price-asc"]')
.pause(500) // Wait for re-render
.assert.textContains('[data-testid="sort-dropdown"]', 'Price: Low to High');
});
});
Custom Commands
// nightwatch/custom-commands/loginViaApi.js
module.exports = {
command: async function (username, password) {
const baseUrl = this.api.launchUrl;
await this.execute(
function (url, user, pass) {
return fetch(`${url}/api/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: user, password: pass }),
})
.then((res) => res.json())
.then((data) => {
document.cookie = `auth_token=${data.token}; path=/`;
return data;
});
},
[baseUrl, username, password]
);
return this.refresh();
},
};
// Usage in tests:
// browser.loginViaApi('[email protected]', 'AdminPass123!')
// .url(`${browser.launchUrl}/admin`)
// .assert.visible('[data-testid="admin-panel"]');
Custom Assertions
// nightwatch/custom-assertions/elementHasCount.js
exports.assertion = function (selector, expectedCount) {
this.message = `Testing if element <${selector}> appears ${expectedCount} times`;
this.expected = expectedCount;
this.pass = function (value) {
return value === expectedCount;
};
this.value = function (result) {
return result.value.length;
};
this.command = function (callback) {
return this.api.elements('css selector', selector, callback);
};
};
// Usage: browser.assert.elementHasCount('[data-testid="cart-item"]', 3)
Multi-Browser Testing
describe('Cross-Browser Compatibility', function () {
it('should render header consistently', function (browser) {
browser
.url(browser.launchUrl)
.waitForElementVisible('[data-testid="site-header"]')
.assert.visible('[data-testid="logo"]')
.assert.visible('[data-testid="nav-menu"]')
.assert.cssProperty('[data-testid="site-header"]', 'display', 'flex');
});
});
Best Practices
- Use Nightwatch's
@elementsyntax from page objects to keep selectors centralized and maintainable. Reference elements as'@usernameInput'rather than repeating raw selectors. - Configure
waitForConditionTimeoutglobally in theglobalssection rather than adding explicit waits to every command. - Enable
test_workersfor parallel execution. Setworkers: 'auto'to use all available CPU cores for maximum throughput. - Use
assertfor hard assertions (fail immediately) andverifyfor soft assertions (continue execution and report at the end). - Capture screenshots on failure via the
screenshotsconfiguration. Enable bothon_failureandon_errorfor comprehensive visual debugging. - Implement custom commands for repetitive multi-step operations like API-based login, clearing sessions, or seeding test data.
- Leverage Nightwatch's built-in Selenium manager -- modern Nightwatch auto-manages browser drivers, eliminating manual driver installation.
- Use
--envflag for multi-browser runs:npx nightwatch --env chrome,firefoxto execute tests across browsers in a single command. - Structure tests by feature domain (auth, checkout, search) rather than by page name. This keeps related behavior tests together.
- Set
retryAssertionTimeoutin globals to automatically retry failing assertions, which handles minor timing issues without explicit waits.
Anti-Patterns
- Using
browser.pause()for synchronization -- Arbitrary sleeps make tests slow and unreliable. UsewaitForElementVisible()orwaitForElementPresent()instead. - Writing tests that depend on execution order -- Tests should be fully independent. Use
before/beforeEachhooks for setup, not prior test results. - Hardcoding selectors in test files -- Duplicating selectors across tests means changing one selector requires updating dozens of files. Use page objects.
- Not using page object commands -- Putting multi-step interactions directly in tests creates duplication. Encapsulate sequences like login flows in page object commands.
- Ignoring test worker compatibility -- Tests that share global variables or browser state fail when run in parallel. Ensure full isolation.
- Using
.verifywhen.assertis needed -- Soft assertions can mask failures. Use.assertfor critical checks and.verifyonly when continuing execution is truly desired. - Skipping
browser.end()in teardown -- Not closing the browser session causes resource leaks and can make subsequent tests fail. - Nesting describes too deeply -- More than two levels of nesting makes tests hard to read and maintain. Keep the hierarchy flat.
- Using complex XPath expressions -- XPath is slower and harder to maintain than CSS selectors. Only use XPath when CSS cannot express the query.
- Not setting up globals for environment config -- Hardcoding URLs, timeouts, and credentials in test files prevents running tests across different environments.
CLI Reference
# Run all tests
npx nightwatch
# Run specific test file
npx nightwatch nightwatch/tests/auth/login.ts
# Run tests in a specific folder
npx nightwatch --group auth
# Run tests matching a tag
npx nightwatch --tag smoke
# Run against specific browser
npx nightwatch --env firefox
# Run multi-browser
npx nightwatch --env chrome,firefox
# Run in parallel
npx nightwatch --parallel
# Run with verbose output
npx nightwatch --verbose
# Run specific test by name
npx nightwatch --testcase "should login with valid credentials"
Setup
# Initialize a new Nightwatch project
npm init nightwatch@latest
# Or install manually
npm install --save-dev nightwatch
# For Chrome testing
npm install --save-dev chromedriver
# For TypeScript support
npm install --save-dev typescript ts-node @types/nightwatch
# Create configuration file
npx nightwatch --init