Addis Ababa Institute of Technology
School of Information Technology and
Engineering
AuthKit Authentication Authentication Library
Test Plan Document
NAME ID SECTION
Netsanet Tewodros UGR/3048/13 2
Lab 1
Input Type or Test Case Input Value Expected Equivalence
test case Description Output Class
Valid Email and Both are Valid eve.holt@reqre HTTP 200 OK, valid
password s.in, pistol Token returned
Missing Email provided eve.holt@reqre HTTP invalid
password and password is s.in, null 400,”Missing
missing password” error
Missing email Password null, pistol HTTP 400, invalid
provided and "Missing email
email is missing or username"
Invalid Email Incorrect format invalid-email, HTTP 400, invalid
Format email pistol "Note: Only
defined users
succeed
registration
"
Both Missing Both the email null, null HTTP 400, invalid
and password is "Missing email
not provided or username"
Email is not netsi@reqres.in HTTP 400, invalid
available in data , pistol "Note: Only
base defined users
succeed
registration
"
Postman screenshot one
Postman screenshot two
Postman screenshot three
Postman screenshoot four
Postman screenshoot five
Postman screenshoot six
Playwright screenshoot
playwright code
import { test, expect, request } from '@playwright/test';
// Test case 1: Valid input
test('Register user with valid credentials and API key', async () => {
const apiContext = await request.newContext({
baseURL: 'https://reqres.in',
extraHTTPHeaders: {
'Content-Type': 'application/json',
'x-api-key': 'reqres-free-v1'
}
})
const response = await apiContext.post('/api/register', {
data: {
email: 'eve.holt@reqres.in',
password: 'pistol'
}
});
expect(response.status()).toBe(200);
const responseBody = await response.json();
console.log('Response:', responseBody);
expect(responseBody).toHaveProperty('id');
expect(responseBody).toHaveProperty('token');
});
// Test case 2: Missing password
test('Register user with missing password', async () => {
const apiContext = await request.newContext({
baseURL: 'https://reqres.in',
extraHTTPHeaders: {
'Content-Type': 'application/json',
'x-api-key': 'reqres-free-v1'
}
});
const response = await apiContext.post('/api/register', {
data: {
email: 'eve.holt@reqres.in',
password: ''
}
});
expect(response.status()).toBe(400);
const responseBody = await response.json();
console.log('Response:', responseBody);
expect(responseBody.error).toBe('Missing password');
});
// Test case 3: Missing email
test('Register user with missing email', async () => {
const apiContext = await request.newContext({
baseURL: 'https://reqres.in',
extraHTTPHeaders: {
'Content-Type': 'application/json',
'x-api-key': 'reqres-free-v1'
}
});
const response = await apiContext.post('/api/register', {
data: {
email: '',
password: 'pistol'
}
});
expect(response.status()).toBe(400);
const responseBody = await response.json();
console.log('Response:', responseBody);
expect(responseBody.error).toBe('Missing email or username');
});
// Test case 4: Invalid email format
test('Register user with invalid email format', async () => {
const apiContext = await request.newContext({
baseURL: 'https://reqres.in',
extraHTTPHeaders: {
'Content-Type': 'application/json',
'x-api-key': 'reqres-free-v1'
}
});
const response = await apiContext.post('/api/register', {
data: {
email: 'not-an-email',
password: 'pistol'
}
});
expect(response.status()).toBe(400);
const responseBody = await response.json();
console.log('Response:', responseBody);
expect(responseBody.error).toBe('Note: Only defined users succeed
registration');
});
// Test case 5: Both fields empty
test('Register user with both fields empty', async () => {
const apiContext = await request.newContext({
baseURL: 'https://reqres.in',
extraHTTPHeaders: {
'Content-Type': 'application/json',
'x-api-key': 'reqres-free-v1'
}
});
const response = await apiContext.post('/api/register', {
data: {
email: '',
password: ''
}
});
expect(response.status()).toBe(400);
const responseBody = await response.json();
console.log('Response:', responseBody);
expect(responseBody.error).toBe('Missing email or username');
// Test case 6: Email not in database
});
test('Register user with email not in database', async () => {
const apiContext = await request.newContext({
baseURL: 'https://reqres.in',
extraHTTPHeaders: {
'Content-Type': 'application/json',
'x-api-key': 'reqres-free-v1'
}
});
const response = await apiContext.post('/api/register', {
data: {
email: 'unknown.user@reqres.in',
password: 'pistol'
}
});
expect(response.status()).toBe(400);
const responseBody = await response.json();
console.log('Response:', responseBody);
expect(responseBody.error).toBe('Note: Only defined users succeed
registration');
});
Lab 2
book prices
Test Type Value description
Minium 0.00 At the lower limit
Low 0.01 above the lower limit
Nominal 50.00 Middle of the range
High 99.99 Below the upper boundary
Maximum 100.00 At the upper boundary
Invalid -0.01 Below minimum and not exit
Invalid 100.01 Above maximum and not
exist
Boundary value test matrix
Test Case ID Test Condition Expected Result Actual Result
TC01 Price = 0 (Minium) No book price
should be below 0
TC02 Price = 0.01 (Low) Book price should
be ≥ 0.01
TC03 Price = 50 (Nominal) Book prices should
be valid and shown
TC04 Price = 99.99 (High) Book prices should
be ≤ 99.99
TC05 Price = 100 Book prices should
(Maximum) not exceed 100
TC06 Price < 0 (Invalid) Should not exist or
cause errors
TC07 Price > 100 (Invalid) Should not exist or
cause errors
Manually
Book Title Price
A Light in the Attic £51.77
Tipping the Velvet £53.74
Soumission £50.10
Sharp Objects £47.82
Sapiens £54.23
The Requiem Red £22.65
The Dirty Little £33.34
The coming women £17.93
import { test, expect } from '@playwright/test';
test('Boundary Value Analysis - Book Prices', async ({ page }) => {
await page.goto('http://books.toscrape.com/');
let pageNumber = 1;
let hasNextPage = true;
while (hasNextPage) {
console.log(`Checking page ${pageNumber}`);
// Take a screenshot of the current page
await page.screenshot({ path: `page_${pageNumber}.png` });
// Extract prices from current page
const prices = await page.$$eval('.product_price .price_color',
elements =>
elements.map(el => parseFloat(el.textContent!.replace('£',
'').trim()))
);
// Boundary checks
for (const price of prices) {
console.log(`Found price: £${price}`);
expect(price).toBeGreaterThanOrEqual(0);
expect(price).toBeLessThanOrEqual(100);
}
// Try to navigate to next page
const nextButton = page.locator('.next > a');
if (await nextButton.count()) {
✅
await Promise.all([
page.waitForURL(/page-\d+\.html/), // Wait for URL change
instead of load state
nextButton.click()
]);
pageNumber++;
} else {
hasNextPage = false;
}
}
console.log('All prices validated against boundary values.');
});
Lab 3
1. Decision Table
Test Cases Logged In Coupon applied Cart ≥ $100 Expected
Discount
TC1 Yes Yes Yes 20%
TC2 No Yes Yes 10%
TC3 Yes No Yes 5%
TC4 No No No 0%
TC5 Yes Yes No 0%
TC6 Yes No No 0%
TC7 No Yes No 0%
TC8 No No Yes 0%
3.2 Designing Test Cases from the Decision Table
TC1: Logged in, Coupon Applied, Cart ≥ $100
● Preconditions: User is logged in, has a valid coupon
● Steps:
1. Log in with valid credentials
2. Add items to cart worth $100+
3. Apply a valid coupon
4. View the discount
● Expected Result: 20% discount is applied
TC2: Not Logged in, Coupon Applied, Cart ≥ $100
● Preconditions: Guest user, has a valid coupon
● Steps:
1. Open the site without logging in
2. Add items to cart worth $100+
3. Apply a valid coupon
4. View the discount
● Expected Result: 10% discount is applied
TC3: Logged in, No Coupon, Cart ≥ $100
● Preconditions: Logged-in user, no coupon used
● Steps:
1. Log in
2. Add items to cart worth $100+
3. Do not apply any coupon
4. View the discount
● Expected Result: 5% discount is applied
TC4: Not Logged in, No Coupon, Cart < $100
● Preconditions: Guest user, no coupon, low cart value
● Steps:
1. Open the site without logging in
2. Add items to cart worth less than $100 (e.g., $50)
3. Do not apply a coupon
4. View the discount
● Expected Result: 0% discount
TC5: Logged in, Coupon Applied, Cart < $100
● Preconditions: Logged in, valid coupon, cart below threshold
● Steps:
1. Log in
2. Add items to cart worth less than $100
3. Apply coupon
4. View the discount
● Expected Result: 0% discount
TC6: Logged in, No Coupon, Cart < $100
● Preconditions: Logged in, no coupon, small cart
● Steps:
1. Log in
2. Add items to cart worth less than $100
3. Don’t apply coupon
4. View the discount
● Expected Result: 0% discount
TC7: Not Logged in, Coupon Applied, Cart < $100
● Preconditions: Guest user, coupon used, cart < $100
● Steps:
1. Open the site without logging in
2. Add items to cart worth less than $100
3. Apply coupon
4. View the discount
● Expected Result: 0% discount
TC8: Not Logged in, No Coupon, Cart ≥ $100
● Preconditions: Guest user, high cart value, no coupon
● Steps:
1. Open site as guest
2. Add items worth $100 or more
3. Don’t apply coupon
4. View the discount
● Expected Result: 0% discount
Playwright code
Code:
import { test, expect } from '@playwright/test';
const testCases = [
{ loggedIn: true, couponApplied: true, cartTotal: 150,
expectedDiscount: 20 },
{ loggedIn: false, couponApplied: true, cartTotal: 150,
expectedDiscount: 10 },
{ loggedIn: true, couponApplied: false, cartTotal: 150,
expectedDiscount: 5 },
{ loggedIn: false, couponApplied: false, cartTotal: 50,
expectedDiscount: 0 },
];
testCases.forEach(({ loggedIn, couponApplied, cartTotal,
expectedDiscount }) => {
test(`loggedIn=${loggedIn}, couponApplied=${couponApplied},
cartTotal=${cartTotal}`, async ({ page }) => {
// STEP 1: Optional login
if (loggedIn) {
await page.goto('https://demo.nopcommerce.com/login');
await page.fill('input#Email', 'netsanettewodros62@gmail.com');
await page.fill('input#Password', '123123123');
await page.click('button.login-button'); // Adjust if needed
await page.waitForLoadState('networkidle');
// STEP 2: Go to Cart Page
await page.goto('https://demo.nopcommerce.com/cart');
// STEP 3: Apply coupon (if needed)
if (couponApplied) {
await page.fill('input[name="discountcouponcode"]',
'DISCOUNT2025');
await page.click('button[name="applydiscountcouponcode"]');
await page.waitForLoadState('networkidle');
}
// STEP 4: Simulate cart total (You need to pre-add enough items
manually for now)
// STEP 5: Get discount info — check HTML class from browser!
const discountElement = await page.locator('.order-total
.discount-value'); // You must inspect & confirm this selector
const discountExists = await discountElement.count();
let actualDiscount = 0;
if (discountExists) {
const text = await discountElement.textContent();
if (text) {
const percentMatch = text.match(/(\d+)%/);
if (percentMatch) {
actualDiscount = parseFloat(percentMatch[1]);
// STEP 6: Check result
expect(actualDiscount).toBe(expectedDiscount);
});
});
Lab 4
Define States and Transitions
States:
A: Start
B: Logged In
C: Invalid Attempt
D: Locked Out (after 3 failures, if supported)
Transitions:
A → B: Valid credentials
A → C: Invalid credentials
C → C: Repeat invalid credentials (up to 2 more)
C → D: Locked out after 3 failed attempts
Graph for lab:
Screen shoot
Code for the lab four
import { test, expect, Page } from "@playwright/test";
const validUser = {
email: `user${Math.floor(Math.random() * 10000)}@example.com`,
password: `Pass${Math.floor(Math.random() * 1000)}!`,
};
const invalidUser = {
email: "invalidUser",
password: "Pass123!",
};
test.describe("Lab 4 – State Transition Testing (Login)", () => {
test.beforeEach(async ({ page }) => {
await page.goto("https://demo.nopcommerce.com/register");
await page.check("#gender-male");
await page.fill("#FirstName", "Test");
await page.fill("#LastName", "User");
await page.fill("#Email", validUser.email);
await page.fill("#Password", validUser.password);
await page.fill("#ConfirmPassword", validUser.password);
await page.click('button:has-text("Register")');
await page.waitForSelector(".result");
});
test("S0 → S1: Valid Login should go to dashboard", async ({ page })
=> {
await gotoLoginPage(page);
await login(page, validUser);
await expect(page.locator(".header-links
.ico-logout")).toBeVisible();
console.log("Transition: S0 → S1 (Success)");
});
test("S0 → S2: Invalid Login should show error", async ({ page }) =>
{
await gotoLoginPage(page);
await login(page, invalidUser);
await expect(page.locator(".message-error")).toBeVisible();
console.log(" Transition: S0 → S2 (Failure)");
});
test("S2 → S1: Recovery with valid login after failure", async ({
page }) => {
await gotoLoginPage(page);
await login(page, invalidUser);
await expect(page.locator(".message-error")).toBeVisible();
await login(page, validUser);
await expect(page.locator(".header-links
.ico-logout")).toBeVisible();
console.log("Transition: S2 → S1 (Recovery)");
});
test("S2 → S3: Too many failures lead to lockout (if supported)",
async ({
page,
}) => {
await gotoLoginPage(page);
for (let i = 0; i < 5; i++) {
await login(page, invalidUser);
await page.waitForTimeout(500);
}
console.log(" Would test lockout if system supports it");
});
});
async function gotoLoginPage(page: Page) {
await page.goto("https://demo.nopcommerce.com/login");
await page.waitForLoadState("networkidle");
}
async function login(page: Page, user: { email: string; password:
string }) {
await page.fill("#Email", user.email);
await page.fill("#Password", user.password);
await page.click('button:has-text("Log in")');
await page.waitForTimeout(1000);
}
Lab 5
5.1 Identify the Use Case
Steps:
1. Go to the site
2. Register as a new user
3. Login
4. Add product(s) to cart
5. Apply a coupon (optional)
6. Proceed to checkout
7. Fill billing/shipping/payment details
8. Confirm the order
9. Verify order success message
5.2. Use Case Scenario Table
Step Action Expected Result
1 Visit homepage Homepage loads
2 Register new user Account is created
3 Login with new credentials Logged in and redirected
4 Add product to cart Product appears in cart
5 Apply coupon (optional) Discount applied if valid
6 Proceed to checkout Checkout page loads
7 Fill required details No validation errors
8 Confirm order Order confirmation page
shown
9 View success message Your order has been
successfully processed!
Code for lab 5
import { test, expect, Page } from "@playwright/test";
test("Lab 5: End-to-End Order Flow (Guest Checkout)", async ({ page })
=> {
await page.goto("https://demo.nopcommerce.com/");
await page.waitForLoadState("networkidle");
await page.click('a[href="/books"]');
await page.waitForLoadState("networkidle");
const addToCartButton = page.locator(".product-item
button.product-box-add-to-cart-button").first();
await addToCartButton.waitFor();
await addToCartButton.click();
await page.waitForSelector(".bar-notification.success");
await page.waitForTimeout(1000);
await page.click('a[href="/cart"]');
await page.waitForLoadState("networkidle");
await page.check("#termsofservice");
await page.click("#checkout");
await page.waitForLoadState("networkidle");
const guestCheckoutButton = page.locator('button:has-text("Checkout
as Guest")');
if (await guestCheckoutButton.isVisible()) {
await guestCheckoutButton.click();
await page.waitForLoadState("networkidle");
}
await page.fill("#BillingNewAddress_FirstName", "Test");
await page.fill("#BillingNewAddress_LastName", "User");
await page.fill(
"#BillingNewAddress_Email",
user${Math.floor(Math.random() * 10000)}@mail.com
);
await page.selectOption("#BillingNewAddress_CountryId", {
label: "United States",
});
await page.fill("#BillingNewAddress_City", "New York");
await page.fill("#BillingNewAddress_Address1", "123 Broadway");
await page.fill("#BillingNewAddress_ZipPostalCode", "10001");
await page.fill("#BillingNewAddress_PhoneNumber", "1234567890");
await page.click('button[name="save"]');
await page.waitForLoadState("networkidle");
await page.waitForSelector('input[name="shippingoption"]');
await page.locator('input[name="shippingoption"]').first().check();
await page.click('button[name="save"]');
await page.waitForLoadState("networkidle");
await page.waitForSelector('input[name="paymentmethod"]');
await page.locator('input[name="paymentmethod"]').first().check();
await page.click('button[name="save"]');
await page.waitForLoadState("networkidle");
await page.waitForSelector('button[name="save"]');
await page.click('button[name="save"]');
await page.waitForLoadState("networkidle");
await page.waitForSelector("button.confirm-order-next-step-button");
await page.click("button.confirm-order-next-step-button");
await page.waitForLoadState("networkidle");
await expect(page.locator(".section.order-completed")).toContainText(
"Your order has been successfully processed!"
);
console.log(" Order completed successfully.");
});
Reflection
1. Which technique was most effective for each system?
For the systems we tested, automation using Playwright was the most effective
technique, especially for repetitive test cases and regression testing. It was
particularly useful on the "Books to Scrape" website where boundary value analysis
(BVA) could be consistently applied to test input ranges. Manual testing was more
useful for exploratory testing or when the system’s UI needed human judgment for
visual or usability issues.
2. What challenges did you face in automation vs manual execution?
One challenge with automation was setting up the testing environment and learning
the Playwright framework itself—it took time to configure scripts and selectors
properly. Also, dynamic elements like pop-ups or time-based events were harder to
handle automatically. In contrast, manual testing was slower and more prone to
human error, especially when running the same test multiple times or across different
browsers. However, it allowed more flexibility in adapting to unexpected behaviors in
the UI.
3. How would you integrate these techniques into a CI/CD testing pipeline?
To integrate these techniques into a CI/CD pipeline, I would first ensure that all
automated test scripts are stored in the project’s version control (e.g., GitHub). Then,
I’d configure the CI/CD tool (like GitHub Actions, Jenkins, or GitLab CI) to run the test
suite every time there is a new commit or pull request. This ensures that new code
doesn’t break existing functionality. Manual tests could be logged separately and
scheduled at key points, like before a major release or UI update, but most functional
and boundary tests would be automated and triggered automatically during the
CI/CD process.