The Testing Academy · Class Notes Wednesday, 14 October (IST)
Live class · study guide

if and else, nested if and the else-if ladder, the switch statement with break, fall-through and grouped cases, and four ways to read user input in JavaScript

The if/else statement, nested if, and when to pick it over the ternary. Then the else-if ladder on a grade calculator, an interview question that is really a lesson in boundary values. Then the switch statement: break, fall-through, grouped cases, switch (true) and strict matching, through eight interview questions. It ends with four ways to read user input, and why Playwright tests need none of them. Files 46 to 62, pushed to the batch repo.

By Pramod Dutta, The Testing Academy. Study notes from the eighth class of Playwright 4x, built from the session recording, the batch repository, which received files 46 to 62 and three cheat-sheet images as the class ended (commit "feat: add chapters 06-08 (if/else, switch, input)"), and the Eraser notes deck. Every file was run with Node 22, the input files in a terminal with real typing, and the output shown is what it prints. The grade calculator gets one boundary wrong as pushed; it is shown as written in class, then with its fix, which reached the repo after class.

01

What this class covered

  • if and else, and what makes them different from the ternary
  • Nested if: a condition inside a condition
  • Ternary or if/else: one line against several
  • The else-if ladder: the grade calculator, and boundary values
  • What goes inside the brackets: comparison, logical and NOT, and always ===
  • The switch statement: case, break and default
  • Eight switch questions: fall-through, default, an API status code, grouped cases, switch (true), duplicate cases and strict matching
  • switch or if/else: which one when
  • Four ways to read user input: prompt, readline, prompt-sync and fs
  • Tasks: five coding exercises in JavaScript
02

if and else

From the class notes: the if/else statement is JavaScript's most basic decision-making tool. It runs a block of code when a condition is true, and optionally a different block when it is false. Some call it a statement, some a condition; both are right. It works like the ternary operator from the last class:

JavaScript
if (condition) {
  // runs if condition is true
} else if (anotherCondition) {
  // runs if anotherCondition is true
} else {
  // runs if ALL conditions are false
}

The Goa example again, now as an if, the first file of chapter 06:

JavaScript
let age = 16;
if(age > 18){
    console.log("Go Goa");
}else{
    console.log("No Goa");
}
Text
No Goa

16 is not greater than 18, so the else block runs.

Case matters. JavaScript keywords are lower case: If and Let are not keywords, and code that uses them does not run at all (If (true) { ... } stops with SyntaxError: Unexpected token '{'). And always use the curly braces, even for a single line.

03

Nested if

Two conditions, one inside the other, as with the nested ternary: over 18, you can go to Goa; over 26 as well, you can also drink.

JavaScript
// 
let age = 27
if (age > 18) {
    console.log("Goa Check!");
    if (age > 26) {
        console.log("Drink and Goa");
    } else {
        console.log("Only Goa, no Drink");
    }
}
else {
    console.log("No Goa");
}
Text
Goa Check!
Drink and Goa

27 is greater than 18, so the outer block runs and prints "Goa Check!". Inside it, 27 is greater than 26 as well, so the inner if prints "Drink and Goa", and neither else runs. In VS Code, right-click and choose Format Document to tidy the indentation, which makes nesting much easier to read.

04

Ternary or if/else?

"Why use if/else when the ternary does the same thing?" Because the ternary is for one line. When each branch has several statements to run, you need if/else:

JavaScript
let age = 20;
let result = age > 21 ? "Drive": "No Able to DRIve";

if(age > 21){
    console.log("Drive");
    console.log("Drink");
    console.log("Go goa");
}else{
    console.log("No Able to DRIve");
}
Text
No Able to DRIve

The ternary on line 2 picks one of two values. The if block runs three statements when the condition is true. One value: ternary. Several statements: if/else.

05

The else-if ladder: the grade calculator

A problem from the batch's coding platform, and an interview question too. The problem statement, from the class notes:

Write a JavaScript program that accepts a student's exam score and determines the corresponding grade using conditional statements (if, else if, and else). The score must be between 0 and 100 (inclusive).

Score range Grade Performance
91 to 100 A Excellent
81 to 90 B Very Good
71 to 80 C Good
61 to 70 D Average
50 to 60 E Needs Improvement
0 to 49 F Fail

The requirements: print the grade, display "Invalid Score" for anything below 0 or above 100, and handle the boundary values correctly: 0, 49, 50, 60, 70, 80, 90 and 100. The samples give the exact output, such as Input: 95 giving Grade A - Excellent, and Input: 105 giving Invalid Score.

The class's solution, the code part of the file (its opening comments repeat the problem statement):

JavaScript
let score = 95;
// user input... 
// score will be int

if (score > 100 || score < 0) {
    console.log("Invalid score");
} else {
    // Score valid

    if (score > 90) {
        console.log(" Grade A - Excellent");
    }
    else if (score > 80 && score <= 90) {
        console.log("Grade B - Very Good");
    }
    else if (score > 70 && score <= 80) {
        console.log("Grade C - Good");
    }
    else if (score > 60 && score <= 70) {
        console.log("Grade D - Average");
    }
    else if (score > 50 && score <= 60) {
        console.log("Grade E - Needs Improvement");
    }
    else {
        console.log("Grade F - Fail");
    }
}
Text
 Grade A - Excellent

How it was built, one decision at a time:

  • The ladder. if, then else if for each band, then else for whatever is left. The first condition that is true wins, and the rest are skipped.
  • Edges inside each band. "If it is exactly 90, do you get A or B?" B, so B is score > 80 && score <= 90, with the = on 90, and A is score > 90.
  • The invalid check comes first. At first a score of -1 printed "Fail", because the else caught it. Hence the check at the top, joined with OR (||), not AND: a score cannot be above 100 and below 0 at once. Only when the score is valid does the ladder run.
  • What is out of scope. Decimals, letters and text such as "Pramod" were not tested, because the requirement says the score is a whole number from 0 to 100. Negative tests are written for what is in scope. Checking that input is a number at all comes later.
A diagram titled The else if Ladder. Four boxes step down to the right, each pointing to a grade badge: if (score > 90) to A, else if (score > 80) to B, else if (score > 70) to C, and a grey else box to F. A banner at the bottom reads: The first true check wins.
From the batch repo's README: the ladder runs top to bottom and stops at the first true condition.

The requirements list the boundary values to test. Run the pushed code at each of them:

SIX GRADE BANDS, AND THE VALUES WHERE THEY MEET F 0-49 E 50-60 D 61-70 C 71-80 B 81-90 A 91-100 -1 101 invalid invalid THE BUG AT 50 score > 50 && score <= 60 50 is not greater than 50, so it falls through to else and prints Grade F - Fail. fix: score >= 50 THE TEXT MUST MATCH TOO " Grade A - Excellent" a leading space at 91 to 100 "Invalid score" a small s at -1 and 101 Boundary values to test: 0, 49, 50, 60, 70, 80, 90, 100, and just outside: -1 and 101.
The grading table from the problem statement. Every edge between two bands is a boundary to test, and the pushed code gets one of them wrong.
Score The problem statement expects The pushed code prints
-1 "Invalid Score" "Invalid score" ✗ text
0 "Grade F - Fail" "Grade F - Fail" ✓
49 "Grade F - Fail" "Grade F - Fail" ✓
50 "Grade E - Needs Improvement" "Grade F - Fail" ✗ grade
51 "Grade E - Needs Improvement" "Grade E - Needs Improvement" ✓
60 "Grade E - Needs Improvement" "Grade E - Needs Improvement" ✓
61 "Grade D - Average" "Grade D - Average" ✓
70 "Grade D - Average" "Grade D - Average" ✓
71 "Grade C - Good" "Grade C - Good" ✓
80 "Grade C - Good" "Grade C - Good" ✓
81 "Grade B - Very Good" "Grade B - Very Good" ✓
90 "Grade B - Very Good" "Grade B - Very Good" ✓
91 "Grade A - Excellent" " Grade A - Excellent" ✗ text
100 "Grade A - Excellent" " Grade A - Excellent" ✗ text
101 "Invalid Score" "Invalid score" ✗ text

One logic bug and two text mismatches. At 50 the problem statement says E, but score > 50 && score <= 60 is false for 50, so it falls through to the else and prints Fail. The repo's README flags it too, and the fix reached the repo about 20 minutes after class, so a fresh clone has score >= 50. The other two are exact-text slips, and they are still in the repo: " Grade A - Excellent" has a leading space, and "Invalid score" has a small s where the statement says Invalid Score. The grade is right, but a judge that compares output text exactly fails both. The version below has all three one-line fixes:

JavaScript
let score = 95;
// user input... 
// score will be int

if (score > 100 || score < 0) {
    console.log("Invalid Score");
} else {
    // Score valid

    if (score > 90) {
        console.log("Grade A - Excellent");
    }
    else if (score > 80 && score <= 90) {
        console.log("Grade B - Very Good");
    }
    else if (score > 70 && score <= 80) {
        console.log("Grade C - Good");
    }
    else if (score > 60 && score <= 70) {
        console.log("Grade D - Average");
    }
    else if (score >= 50 && score <= 60) {
        console.log("Grade E - Needs Improvement");
    }
    else {
        console.log("Grade F - Fail");
    }
}
Score Expected The fixed code prints
-1 "Invalid Score" "Invalid Score" ✓
0 "Grade F - Fail" "Grade F - Fail" ✓
49 "Grade F - Fail" "Grade F - Fail" ✓
50 "Grade E - Needs Improvement" "Grade E - Needs Improvement" ✓
51 "Grade E - Needs Improvement" "Grade E - Needs Improvement" ✓
60 "Grade E - Needs Improvement" "Grade E - Needs Improvement" ✓
61 "Grade D - Average" "Grade D - Average" ✓
70 "Grade D - Average" "Grade D - Average" ✓
71 "Grade C - Good" "Grade C - Good" ✓
80 "Grade C - Good" "Grade C - Good" ✓
81 "Grade B - Very Good" "Grade B - Very Good" ✓
90 "Grade B - Very Good" "Grade B - Very Good" ✓
91 "Grade A - Excellent" "Grade A - Excellent" ✓
100 "Grade A - Excellent" "Grade A - Excellent" ✓
101 "Invalid Score" "Invalid Score" ✓
06

What goes inside the brackets

From the class's cheat sheet for if:

  • A condition can use the comparison operators, the logical ones (&&, ||) and NOT (!), and combinations of them, such as user === "TTA" && isActive === true.
  • The common mistake: using == where you mean ===. From the class notes, the interview question: what is the difference between if (x == 5) and if (x === 5) in testing? Always use ===. == converts types first, so "5" == 5 is true, while "5" === 5 is false. In QA, strict equality catches type bugs.
  • Real uses ahead: checking a username and password, a page theme, and conditions inside Playwright tests.
07

The switch statement

From the class notes: switch evaluates an expression once and compares it against several case values. When a case matches, its code runs. It is cleaner than a long else-if chain when you are comparing a single value against many options, and it uses strict comparison (===).

JavaScript
switch (expression) {
    case value1:
        // code block
        break;       // IMPORTANT: stops fall-through
    case value2:
        // code block
        break;
    default:
        // runs if no case matches (optional)
}

The first file of chapter 07, with day 1 for Monday, 2 for Tuesday and so on:

JavaScript
let day = 2;
//. day 1 - mon, 2 tue...

switch(day){
    case 1: 
        console.log("Monday");
        break;
    case 2:
        console.log('Tue');
        let a = 10;
        let b = 30;
        console.log(a + b);
        break;
    case 3:
        console.log('Wed');
        break;
    case 4:
        console.log('Thur');
        break;
    case 5:
        console.log('Fri');
        break;
    case 6:
        console.log('Sat');
        break;
    case 7:
        console.log('Sun');
        break;
    default:
        console.log("No idea which day it is");
}

Text
Tue
40

day is 2, so case 2 runs, and a case can hold several statements: it prints Tue, adds a and b, prints 40, and break leaves the switch. A tip from class: select a few lines and run just the selection, which runs only that part of the file.

A diagram titled switch: Don't Forget break. Left column, With break: case 1 Monday, case 2 Tuesday highlighted as the match, case 3 Wednesday, and a stop sign labelled break: exit. Right column, Without break: from case 2 Tuesday, arrows run down through case 3 Wednesday and default: Invalid, all highlighted. A note at the bottom reads: switch matches with ===.
From the batch repo's README: with break, only the matching case runs; without it, every case below runs too.
08

Eight switch questions

1. No breaks at all.

JavaScript
let day = 2;
switch (day) {
    case 0:
        console.log("Sunday - Rest Day");
    case 1:
        console.log("Monday - Sprint Planning");
    case 2:
        console.log("Tuesday - Development");
    case 3:
        console.log("Wednesday - Code Review");
    case 4:
        console.log("Thursday - Testing");
    case 5:
        console.log("Friday - Deployment & Retro");
    case 6:
        console.log("Saturday - Rest Day");
    default:
        console.log("Invalid day value");
}
Text
Tuesday - Development
Wednesday - Code Review
Thursday - Testing
Friday - Deployment & Retro
Saturday - Rest Day
Invalid day value

The match is case 2, but with no break the code falls through and runs every case below it, default included. From the class notes: "This is the #1 switch bug." Always use break, unless falling through is what you want, and then say so in a // fall-through comment.

2. No match.

JavaScript
let day = 10;
switch (day) {
    case 0:
        console.log("Sunday - Rest Day");
        break;
    case 1:
        console.log("Monday - Sprint Planning");
        break;
    case 2:
        console.log("Tuesday - Development");
        break;
    case 3:
        console.log("Wednesday - Code Review")
        break;
    case 4:
        console.log("Thursday - Testing");
        break;
    case 5:
        console.log("Friday - Deployment & Retro");
        break;
    case 6:
        console.log("Saturday - Rest Day");
        break;
    default:
        console.log("Invalid day value");
}
Text
Invalid day value

Nothing matches 10, so default runs.

3. A real one, from API testing.

JavaScript
let responseCode = 404;
switch (responseCode) {
    case 200:
        console.log("200 Ok");
        break;
    case 404:
        console.log("404 Not found!");
        break;
    default:
        console.log("Not status code match");
}
Text
404 Not found!

4. Grouped cases. Several values that need the same handling can share one block:

JavaScript
let browser = "Brave";

switch (browser) {
    case "Chrome":
    case "Edge":
    case "Brave":
    case "Opera":
        console.log("Chromium Project!");
        break;
    case "Firefox":
        console.log("Mozilla Project!");
        break;
    case "Safari":
        console.log("Apple browser - uses JavaScriptCore engine");
        break;
    default:
        console.log("Unknown browser - manual testing needed");

}
Text
Chromium Project!

"Brave" matches the third label and falls through the empty cases to the shared console.log. The grouping is real: Chrome, Edge, Brave and Opera are all built from Chromium, the open-source browser project, while Firefox comes from Mozilla and Safari from Apple.

5. A tricky one.

JavaScript
let fruit = "banana";
switch (fruit) {
    case "apple":
        console.log("Apple selected");
    case "banana":
        console.log("Banana selected");
    case "cherry":
        console.log("Cherry selected");
    case "date":
        console.log("Date selected");
    default:
        console.log("Default reached");
}
Text
Banana selected
Cherry selected
Date selected
Default reached

"banana" matches, and with no break after it, cherry, date and the default all run too.

6. switch (true). Ranges in a switch:

JavaScript
let testScore = 85;
switch (true) {
    case (testScore >= 95):
        console.log("Outstanding - Top performer");
        break;
    case (testScore >= 85):
        console.log("Excellent - Above expectations");
        break;
    case (testScore >= 70):
        console.log("Good - Meets expectations");
        break;
    case (testScore >= 50):
        console.log("Needs Improvement");
        break;
    default:
        console.log("Unsatisfactory - Requires training");
}
Text
Excellent - Above expectations
SWITCH (TRUE): EVERY CASE IS A CONDITION, COMPARED WITH TRUE let testScore = 85; switch (true) CASE 1 testScore >= 95 85 >= 95 is false: no match RUNS "Outstanding ..." skipped no CASE 2 testScore >= 85 85 >= 85 is true: a match RUNS "Excellent ..." printed, then break match CASE 3 testScore >= 70 never evaluated RUNS "Good ..." not reached no CASES 4 AND DEFAULT >= 50, then default switch compares with ===, so a case must produce exactly true to match
File 56, the score question. switch (true) turns each case into a condition, checked top to bottom; the first one that is exactly true runs, and break stops the rest, as in an else-if ladder.

7. The same case twice.

JavaScript
let x = 10;
switch (x) {
    case 10:
        let b1 = 1;
        console.log(b1);
        break;
    case 10:
        let b2 = 2;
        console.log(b2);
        break;
    default:
        console.log("d");

}
Text
1

The first case 10 wins, and the second can never run. JavaScript allows the duplicate without complaint; in Java, the same code is a compile error. The repo's README points out why the variables are named b1 and b2: all the cases share one scope, so let b in both would stop the file with SyntaxError: Identifier 'b' has already been declared.

8. The one that comes up most.

JavaScript
let status = 0;
console.log(typeof status);
switch (status) { // // === Strict, value and data type both of them shoul be same.

    case false:
        console.log("false matched");
        break;
    case 0:
        console.log("0 matched");
        break;
}
Text
number
0 matched

status is the number 0. With loose equality, 0 == false is true, but switch compares with ===, and 0 is not strictly equal to false. So case false is skipped, and case 0 matches.

09

switch or if/else?

The rule from the class's cheat sheet:

  • switch when one value is compared against many exact values: a status code, a browser name, a day number.
  • if/else when the logic is complex, or conditions are ranges or combinations.

And its common mistakes: forgetting break; using switch for complex logic; assuming switch matches loosely, when it uses ===; leaving out default; and repeating code that grouped cases could share.

10

Four ways to read user input

"How do I take input from the user?" was the question all class. The first thing to know: JavaScript was never designed to read input from a terminal. It was made for the browser. The ways that exist, from the class notes:

  1. prompt()
  2. readline, a Node.js module
  3. prompt-sync, a Node.js package
  4. fs.readFileSync(), from Node.js
A diagram titled Getting User Input, with four cards: prompt(), Browser only, under a browser dialog icon; readline, Built into Node, under a terminal with a speech bubble; prompt-sync, npm package, under a parcel box; and fs.readFileSync(0), Reads piped stdin, under a terminal with an arrow pointing in.
From the batch repo's README: the four ways to read input, and where each one comes from.

prompt(), browser only.

JavaScript
let num = prompt("Enter a number:");
num = Number(num);  // convert string to number

if (num % 2 === 0) {
    console.log(num + " is Even");
} else {
    console.log(num + " is Odd");
}

In the browser console it opens a dialog; 34 printed "34 is Even". Run the same file with Node and it stops at once:

Text
08_chapter_UserInput/59_UI.js:1
let num = prompt("Enter a number:");
          ^

ReferenceError: prompt is not defined

prompt() belongs to the browser. Node.js runs JavaScript outside the browser, so the function does not exist there, and Node offers its own modules instead.

readline, built into Node.

JavaScript
const readline = require('readline');

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

rl.question("Enter a number: ", (input) => {
       let num = Number(input);

    if (num % 2 === 0) {
        console.log(num + " is Even");
    } else {
        console.log(num + " is Odd");
    }

    rl.close(); 

});
Text
Enter a number: 34
34 is Even

It works, but the answer arrives inside a callback and you must close the interface yourself. The instructor's verdict: it looks difficult, and it is not the one the batch will use.

prompt-sync, the easy one. It is a package, so the folder first has to become a Node.js project:

Terminal
npm init
npm install prompt-sync

npm init asks a few questions and creates package.json. npm install then adds prompt-sync to it. The file:

JavaScript
const prompt = require("prompt-sync")();
let num = Number(prompt("Enter a number"));
console.log(num);

Run with node, typing 90 and Enter, the terminal shows:

Text
Enter a number90
90

The first version in class kept failing, and a learner spotted why: the () after require("prompt-sync") was missing. The package exports a function that builds the prompt, so it has to be called. Leave the () off and the file fails with TypeError: Cannot create property 'autocomplete' on string 'Enter a number'. Two more things worth knowing: the prompt text has no trailing space, so what you type runs straight on from it, and prompt-sync reads from the terminal itself, so it needs a real terminal window.

fs.readFileSync(0), how the coding platform does it.

JavaScript
console.log("Enter the number!");
const data = require('fs').readFileSync(0, 'utf8');
console.log("Hi", data);

// In the terminal:
// Type 15
// Press Enter
// Press Ctrl+D

Type 15, press Enter, then press Ctrl+D:

Text
Enter the number!
15
Hi 15

0 means standard input, and readFileSync reads it to the end. Typing by hand, the end only comes when you press Ctrl+D. This is how the batch's coding platform reads input.

FOUR WAYS TO READ INPUT, AND WHERE EACH ONE WORKS In the browser the console in Chrome, for example WORKS prompt("Enter a number:") opens a dialog; 34 printed 34 is Even prompt() belongs to the browser window, not to JavaScript itself. In Node.js node file.js in a terminal: JavaScript running outside the browser ERROR prompt() ReferenceError: prompt is not defined BUILT INTO NODE readline rl.question(...) hands the answer to a callback; then rl.close() NPM PACKAGE prompt-sync npm install prompt-sync, then require("prompt-sync")() BUILT INTO NODE fs.readFileSync(0, 'utf8') reads everything typed until Ctrl+D; the coding platform reads input this way
Chapter 08 in one picture. prompt() is a browser feature, so Node needs one of the other three. In Playwright tests you use none of them: the data comes from files.

Which one to use? For Playwright, none. A test never stops to ask for input. Its data comes from files: CSV, JSON or Excel. If an interviewer asks for user input in JavaScript, use prompt-sync or fs.

11

Tasks and announcements

  • Coding exercises. Five on the coding platform, all in JavaScript, not Java: Hello World, reading user input, printing a table, FizzBuzz, and the grade calculator. Meeti posts them with a screenshot. Leap year and a triangle classifier come later.
  • The increment and decrement task from the last class gets one more day.
  • Coming up: let, var and const, hoisting and the temporal dead zone (TDZ). An extra class on AI is being planned.
  • The push. The class's code went up with a single command, /go, which updates the README and pushes.
  • Doubts go in today's doubt thread; the instructor answers them there after class.

Task, five exercises. Solve Hello World, user input, the table, FizzBuzz and the grade calculator on the coding platform, in JavaScript. For the grade calculator, test every boundary value in the problem statement before you submit.