Kickstart UI & API Testing in 60s with Super Pancake Automation
Super Pancake Automation is a modern, lightning-fast framework that uses the Chrome DevTools Protocol to test web UIs and APIs — with zero boilerplate, beautiful HTML reports, and instant setup.
In this article, I’ll show you how to set up your first project in under a minute using super-pancake-automation.
npm link: https://www.npmjs.com/package/super-pancake-automation
github link: https://github.com/pradapjackie/super-pancake
Initialize Your Project
Open your terminal and run:
npx super-pancake-automation@latest init test-pancakeThis command scaffolds a fully working project with sample tests and everything preconfigured.
You’ll be guided through an interactive setup:
-MacBook-Pro WebstormProjects % npx super-pancake-automation@latest init super-pancake
Need to install the following packages:
super-pancake-automation@2.7.2
Ok to proceed? (y) y
🚀 Creating Super Pancake automation project: super-pancake-automation
📝 Let's configure your project preferences:
1. Browser Mode:
○ 1. Headless (faster, no GUI)
● 2. Headed (visible browser window)
Enter your choice (1-2) [2]: 1
2. Screenshot Capture:
● 1. Enabled (capture on failure and success)
○ 2. Only on failure
○ 3. Disabled
Enter your choice (1-3) [1]: 1
3. Sample Tests: Will generate proven working examples (sample.test.js + ui-website.test.js)
4. Test Runner UI:
● 1. Interactive UI enabled
○ 2. Command line only
Enter your choice (1-2) [1]: 1
5. Test Reports:
● 1. HTML reports with screenshots
○ 2. JSON reports
○ 3. Console output only
Enter your choice (1-3) [1]: 1
✨ Creating project with your preferences...
✅ Successfully created super-pancake-automation!
📦 Installing dependencies...
Running npm install...
...
✅ Dependencies installed successfully!
🚀 Next steps:
cd super-pancake-automation
npm test
🎯 Key Features Enabled:
✅ Sequential test execution (prevents Chrome port conflicts)
🚀 Custom Super Pancake test runner
🔧 Environment variable support (HEADED=true, DEBUG=true)
📸 Screenshots will be saved to ./screenshots/
📊 HTML test reports will be generated as ./test-report.html
🎯 Run "npm run test:ui" for interactive testing with Vitest UI
⚡ Tests configured for sequential execution (headless by default)
👀 Use "npm run test:headed" or "HEADED=true npm test" for visible browser
📝 Generated proven working sample tests (sample.test.js + ui-website.test.js + api.test.js)
🛠️ Available Commands:
npm test # Run all tests
npm run test:ui # Vitest UI interface
Happy testing! 🥞Project Structure
After initialization, your project will look like this:
josestepha@Joses-MacBook-Pro test-pancake % tree
.
├── automationTestReport.html
├── package-lock.json
├── package.json
├── screenshots
├── scripts
│ └── super-pancake-test.js
├── super-pancake.config.js
├── tests
│ ├── sample.test.js
│ └── ui-website.test.js
└── utils
└── test-setup.js✍️ Writing a Simple Test
Here’s a basic UI test using the built-in CDP functions:
import { describe, it, beforeAll, afterAll } from 'vitest';
import {
// Simplified test setup
createTestEnvironment,
cleanupTestEnvironment,
// DOM operations
enableDOM,
navigateTo,
getText,
waitForSelector,
takeElementScreenshot,
// Assertions
assertEqual,
assertContainsText,
// Reporting
writeReport,
// Port utilities
findAvailablePort
} from 'super-pancake-automation';
let testEnv;
describe('Super Pancake NPM Website Tests', () => {
beforeAll(async () => {
console.log('🌐 Setting up Super Pancake NPM Website test...');
// Find available port dynamically to avoid conflicts
const port = await findAvailablePort(9223, 10);
console.log(`🔍 Using dynamic port: ${port}`);
testEnv = await createTestEnvironment({
headed: process.env.SUPER_PANCAKE_HEADLESS === 'false', // Respect UI setting: false=headless, true=headed
port: port, // Use dynamically allocated port
testName: 'Super Pancake NPM Website Tests'
});
await enableDOM(testEnv.session);
}, 30000);
afterAll(async () => {
// Keep browser open for 5 seconds to see results
console.log('⏳ Keeping browser open for 5 seconds...');
await new Promise(resolve => setTimeout(resolve, 5000));
await cleanupTestEnvironment(testEnv, 'Super Pancake NPM Website Tests');
writeReport();
console.log('📄 UI test report generated');
});
it('should navigate to Super Pancake NPM page', async () => {
console.log('🌐 Testing NPM package page navigation...');
// Navigate to the npm package page
await navigateTo(testEnv.session, 'https://www.npmjs.com/package/super-pancake-automation');
// Wait for page title to load
const h1Element = await waitForSelector(testEnv.session, 'h1', 15000);
// Take screenshot of the NPM page
await takeElementScreenshot(testEnv.session, 'body', './npm-page-screenshot.png');
console.log('📸 NPM page screenshot saved');
console.log('✅ Successfully navigated to NPM package page');
});
it('should verify package information', async () => {
console.log('🔍 Verifying package information...');
try {
// Get package name
const h1Element = await waitForSelector(testEnv.session, 'h1', 5000);
const title = await getText(testEnv.session, h1Element);
console.log('📦 Package title:', title);
// Verify it contains our package name
assertContainsText(title, 'super-pancake-automation', 'Page should show correct package name');
// Look for version information
const versionElement = await waitForSelector(testEnv.session, '[data-testid="version-number"]', 5000);
if (versionElement) {
const version = await getText(testEnv.session, versionElement);
console.log('📋 Current version:', version);
}
console.log('✅ Package information verified');
} catch (error) {
console.log('⚠️ Some package info checks failed (this is normal for new packages):', error.message);
}
});
it('should check package statistics', async () => {
console.log('📊 Checking package statistics...');
// Simple screenshot-based check for package page content
await takeElementScreenshot(testEnv.session, 'body', './package-stats-screenshot.png');
console.log('📸 Package statistics screenshot saved');
console.log('✅ Package statistics check completed');
}, 10000);
it('should verify README content', async () => {
console.log('📖 Checking README content...');
// Simple screenshot-based check for README section
await takeElementScreenshot(testEnv.session, 'body', './readme-screenshot.png');
console.log('📸 README section screenshot saved');
console.log('✅ README verification completed');
}, 10000);
it('should test install command visibility', async () => {
console.log('💻 Checking install command...');
// Take screenshot of install section
await takeElementScreenshot(testEnv.session, 'body', './npm-install-section.png');
console.log('📸 Install section screenshot saved');
console.log('✅ Install command section verified');
});
it('should navigate to GitHub repository', async () => {
console.log('🔗 Testing GitHub repository link...');
// Take screenshot showing the full page with any GitHub links
await takeElementScreenshot(testEnv.session, 'body', './github-links-screenshot.png');
console.log('📸 GitHub links screenshot saved');
console.log('✅ GitHub repository link test completed');
});
});
Here is the configuration for super
export default {
// Browser configuration
browser: {
headless: process.env.HEADED !== 'true',
devtools: process.env.DEBUG === 'true',
slowMo: 0
},
// Sequential test execution settings
execution: {
// Run tests sequentially to avoid Chrome port conflicts
sequential: true,
// Vitest-specific settings for sequential execution
vitest: {
pool: 'forks',
poolOptions: {
forks: {
singleFork: true,
},
},
fileParallelism: false,
sequence: {
concurrent: false,
shuffle: false,
},
bail: 1, // Stop on first failure
retry: 1, // Retry failed tests once
}
},
// Test configuration
test: {
timeout: 30000,
retries: 1
},
// Screenshot configuration
screenshot: {
enabled: true,
path: './screenshots',
onFailure: true,
onSuccess: true,
quality: 90,
fullPage: true
},
// Report configuration
report: {
enabled: true,
format: 'html',
path: './test-report.html',
autoOpen: false
},
// UI configuration
ui: {
enabled: true,
port: 3000
},
// Logging configuration
logging: {
console: true,
network: false,
level: 'info'
},
// Timeouts
timeouts: {
testTimeout: 30000,
pageTimeout: 30000,
elementTimeout: 10000
}
};API Test Samples
import { describe, it, beforeAll, afterAll } from 'vitest';
import {
// API testing functions
sendGet,
sendPost,
setAuthToken,
withBaseUrl,
timedRequest,
// Assertions for API responses
assertStatus,
assertHeader,
assertBodyContains,
assertResponseTime,
validateSchema,
assertJsonPath,
// Utilities
buildUrlWithParams,
logResponse
} from 'super-pancake-automation';
describe('Super Pancake API Tests', () => {
beforeAll(async () => {
console.log('🚀 Setting up API tests...');
// Optional: Set base URL or auth token
// setAuthToken('your-api-token');
});
afterAll(async () => {
console.log('🧹 API tests completed');
});
it('should perform a GET request to JSONPlaceholder API', async () => {
console.log('🌐 Testing GET request...');
// Make a GET request to a public API
const response = await sendGet('https://jsonplaceholder.typicode.com/posts/1');
// Assert response status
assertStatus(response, 200);
// Assert response contains expected data
assertBodyContains(response, 'userId', 1);
assertBodyContains(response, 'id', 1);
// Verify response structure
const expectedSchema = {
type: 'object',
properties: {
userId: { type: 'number' },
id: { type: 'number' },
title: { type: 'string' },
body: { type: 'string' }
},
required: ['userId', 'id', 'title', 'body']
};
validateSchema(response.data, expectedSchema);
console.log('✅ GET request test passed');
});
it('should test POST request with data', async () => {
console.log('📤 Testing POST request...');
const postData = {
title: 'Super Pancake Test Post',
body: 'This is a test post created by Super Pancake automation',
userId: 1
};
// Make a POST request
const response = await sendPost('https://jsonplaceholder.typicode.com/posts', postData);
// Assert response status for creation
assertStatus(response, 201);
// Assert the posted data is in response
assertBodyContains(response, 'title', 'Super Pancake Test Post');
assertBodyContains(response, 'userId', 1);
// Check that an ID was assigned
const responseId = response.data.id;
if (typeof responseId !== 'number' || responseId <= 0) {
throw new Error(`Expected positive number ID, got: ${responseId}`);
}
console.log('✅ POST request test passed');
});
it('should test response time performance', async () => {
console.log('⏱️ Testing API response time...');
// Use timed request to measure performance
const timedResponse = await timedRequest(() =>
sendGet('https://jsonplaceholder.typicode.com/posts')
);
// Assert response time is reasonable (under 3 seconds)
assertResponseTime(timedResponse, 3000);
// Assert we got multiple posts
const posts = timedResponse.data;
if (!Array.isArray(posts) || posts.length === 0) {
throw new Error('Expected array of posts');
}
console.log(`📊 Response time: ${timedResponse.duration}ms`);
console.log(`📋 Received ${posts.length} posts`);
console.log('✅ Performance test passed');
});
it('should test URL building with parameters', async () => {
console.log('🔗 Testing URL parameter building...');
const baseUrl = 'https://jsonplaceholder.typicode.com/posts';
const params = { userId: 1, _limit: 5 };
// Build URL with parameters
const urlWithParams = buildUrlWithParams(baseUrl, params);
console.log('🌐 Built URL:', urlWithParams);
// Make request with parameters
const response = await sendGet(urlWithParams);
assertStatus(response, 200);
// Should get posts from user 1, limited to 5
const posts = response.data;
if (posts.length > 5) {
throw new Error(`Expected max 5 posts, got ${posts.length}`);
}
// All posts should be from userId 1
for (const post of posts) {
if (post.userId !== 1) {
throw new Error(`Expected userId 1, got ${post.userId}`);
}
}
console.log(`✅ URL parameters test passed - got ${posts.length} posts`);
});
it('should test error handling for invalid endpoints', async () => {
console.log('❌ Testing error handling...');
try {
// Try to access a non-existent endpoint
await sendGet('https://jsonplaceholder.typicode.com/nonexistent');
throw new Error('Expected request to fail');
} catch (error) {
// This should happen - the endpoint doesn't exist
console.log('✅ Correctly handled 404 error:', error.message);
}
});
it('should log and inspect API response', async () => {
console.log('🔍 Testing response logging...');
const response = await sendGet('https://jsonplaceholder.typicode.com/users/1');
// Log the full response for inspection
logResponse(response);
// Assert basic structure
assertStatus(response, 200);
assertBodyContains(response, 'name', 'Leanne Graham');
assertBodyContains(response, 'email', 'Sincere@april.biz');
// Test nested object access
assertJsonPath(response.data, 'address.city', 'Gwenborough');
assertJsonPath(response.data, 'company.name', 'Romaguera-Crona');
console.log('✅ Response logging test passed');
});
});Run Your First Test
Navigate to your new folder:
cd test-pancakeThen run:
npm testYou’ll see output like:
Test Files 2 failed | 1 passed (3)
Tests 1 failed | 7 passed | 6 skipped (14)
Start at 19:19:59
Duration 15.50s (transform 41ms, setup 0ms, collect 415ms, tests 25.63s, environment 0ms, prepare 149ms)Interactive Runner
Want a visual test runner?
npx super-pancake-uiThis opens an interactive test dashboard where you can see live results, inspect test cases, and view logs.
The report will look like this.
⚙️ CI-Ready with HTML Reports
Super Pancake is great for CI/CD too. It generates HTML reports and screenshot logs automatically, so debugging failures is fast and visual.
You can integrate it with GitHub Actions, GitLab CI, or Jenkins using:
Conclusion
If you’re tired of over-complicated test setups and flaky drivers, Super Pancake is worth checking out. With just one command, you get everything — CDP-powered browser control, built-in assertions, HTML reports, and headless test execution.
Try it once, and you might just pancake-flip your testing workflow. 🥞
If you enjoyed this, follow me for more on testing, APIs, CI/CD, and dev automation!
I have created a project on GitHub and added the code here.
Feel free to hit clap if you like the content. Happy Automation Testing :) Cheers. 👏
If you’d like to support my work, please leave a good rating for my Chrome plugin here and my Firefox plugin here.
If you found this helpful, leave a ⭐ on GitHub or try contributing a test case!
