Search for:
ToDoList Blog banner

Exciting Web Development Journey – To Do List

Hi there! Let’s continue our Web Development Journey we started in the previous post when we learned HTML, CSS, and JavaScript concepts. We had the opportunity to build a proof-of-concept web page to work with Sharing is The Key Website light and dark themes. I will assume you know some of the conventions, tricks, concepts, and tools we taught in the previous post for better productivity.

SITK To Do List

Scenario

We will make a web app for a to-do list. We’ll be able to use this app to track whatever we want: cool programming tricks that you want to learn, places to go, songs to listen to or learn to play, or something as basic as things to pick up at the stores. It will be possible to add and remove to-do items from the list and save them so they reload for you later.

Learning goals

  1. Getting to know more HTML elements such as Form and input text fields
  2. Styling using global variables, Flexbox module, and many other CSS resources
  3. Creating your own JavaScript functions
  4. Listening for different user actions on a web page
  5. Using functions together to write code more professionally.
  6. Saving user information between visits to your web page

What you will need

  1. VS Code, the cross-platform code editor. Or one of your preference.
  2. Docker Desktop latest stable version. You can see how to set it up here.
  3. Familiarity with at least one programming language. You don’t need to be an expert.
  4. A computer running one of the following:
    • Windows: Windows 10 Home or Professional
    • Mac: macOS 10.9 or later
    • Linux: Ubuntu, Debian, Red Hat, Fedora, or SUSE

Project structure

Let’s clone the previous post project to save some time and learn more about git CLI.

In your terminal, type the following command and hit Enter.

git clone https://github.com/sitknewnormal/SITK-themes.git "SITK-ToDoList"

This command will pull SITK-themes source code from our Git Hub repository and copy it into the SITK-ToDoList new folder.

SITK-TodoList project structure

Move into the SITK-ToDoList project folder, remove git source control, and open the VS Code by running the following commands.

cd .\SITK-ToDoList\; Remove-Item -LiteralPath ".git" -Force -Recurse; code .

opening SITK-ToDoList in VS code

It will open SITK-ToDoList in VS Code. If you remember from the previous post, it will ask you to reopen the project into a Dev Container, but for now, let’s not do this because I want to introduce you to an exciting VS Code extension.

SITK-ToDoList in VS Code

Let’s open the devcontainer.json file, make some changes and save them:

  • In the extensions values, replace "tht13.html-preview-vscode" by "ritwickdey.liveserver" (Live Server). Live Server extension launches a local development server with live reload feature for static and dynamic pages;
  • Change the name to “SITK – ToDo List.”
making changes to dev container config file

Now that we have everything in place, close VS Code and reopen it, but at this time, choose to reopen the project into the Docker Dev Container when asked to do so.

Let’s code HTML

When the Dev Container is up and running, and the project is ready to be edited, let’s go to the index.html and make some changes.

First, click on Go Live in the status bar to start our Live Server.

opening Live Server

It will open the web page in your default browser. Let’s put the VS Code and the browser windows side-by-side to see the reflection of what we are coding in the browser as soon as we save the changes, and let’s close the VS Code Explorer to get more room to code.

VS Code and browser side by side

Let’s get rid of everything between the opening <body> and closing </body> body element except for the theme switcher button element and save it.

SITK-ToDoList clearing out everything in the body

Let’s give our app an important header To-do List using <h1> element at the beginning of the body and save it.

SITK-ToDoList adding H1

We will need a paragraph <p> right after the header to inform the user to mark an item as completed by double-clicking on it. Please save it to see it in the browser.

SITK-ToDoList adding a paragraph with some instruction

To add items to our To-do List, we will need a Form with an input text element and a button. An HTML Form is used to collect user input. The user input is most often sent to a server for processing, but this is not our case for this post. Therefore copy the code below to a new line right after the paragraph we’ve just added.

        <form name="todo-adder">
            <input type="text" id="todo-entry-box" />
        </form>
        <button id="add-btn" class="pretty-button">Add</button>

Notice that we  named our Form name="todo-adder", we’ve given an identification id="todo-entry-box" to our input text, we did it to the button id="add-btn" as well, and we also created a class class="pretty-button" for future CSS styling. It is essential to keep in mind that naming, giving identification to HTML elements is always a good practice and will help you style them uniquely. Classes are often used to style a group of elements. Furthermore, the naming convention for CSS attributes such as name, id, and class suggests using lowercase and the hyphen for composed names.

SITK-ToDoList adding a form input text and a button

Now let’s break a line to separate the Form from the items, and let’s create an ordered list container so the user can see the item they will add to the list. Please copy the code below to a new line after the Add button.


        <br />
        <ol id="todo-list">
        </ol>

We gave an ID id="todo-list" to the To-do List for future CSS styling.

SITK-ToDoList adding ordered list

Next, we will create three buttons and wrap them in a div element. When it comes to productivity, VS Code is one of the best editors. The Emmet feature can help you with repetitive and boring coding tasks, and no extension is needed. Take a look at the video below to have a taste of VS Code editor’s power.

Let’s give them some IDs and texts accordingly to the chunk of code below.


    <div id="control-wrapper">
        <button id="clear-completed-btn" class="pretty-button">Clear Completed</button>
        <button id="empty-btn" class="pretty-button">Empty List</button>
        <button id="save-btn" class="pretty-button">Save List</button>
    </div>
SITK-ToDoList adding three buttons

Let’s do one more cool thing! To make our To-do List look better, we will wrap the elements from line 12 (Header 1) to 25 (three buttons div) in a div to style it. We will select lines 12 to 25, type the shortcut Ctrl+Shift+P to access the command palette, type Emmet: Wrap with Abbreviation, when it opens to enter the abbreviation, type div.todo-wrapper and hit Enter.

The final HTML code should look like this.

SITK-ToDoList final HTML code

As we could notice, we didn’t touch the head section where we still can see the style sheet linked, and we didn’t change the script tag where we still keep our JavaScript also linked.

Styling HTML with CSS

We already have our CSS from the previous post set up, which already supports light and dark themes, and we will keep and extend it. By the way, spending some time styling an application is very important and allows a better user experience.

Let’s do some cleaning before we style the new HTML elements we added.

First, let’s get rid of li, ul and .list selectors since we don’t have them anymore in the HTML file. Plus, let’s add three more colour variables in the root element. Please copy the code below at the end of the root element and save it.


    --laser1-light: #e5d9b6;
    --light-gray: #3a3a3a;
    --completed: #666666
SITK-ToDoList adding variables to the root element

We will use --laser1-light and --light-gray to style the to-do wrapper div’s background colour when we switch between light and dark themes, respectively. Place each variable inside light and dark themes selectors.

SITK-ToDoList placing new variables in dark and light themes

Let’s change the theme switcher button absolute position to the web page’s right top corner by changing the top key to 0px, replacing left by right key and set it also to 0px, in the .btn selector.

SITK-ToDoList setting theme switcher button to top right corner

Next, we will style the to-do wrapper div setting the background colour, border-radius, top, left and right margin and padding. Please copy the code below to the end of the CSS file. Notice that we are already using the --todo-wrapper-bg variable so the background will assume --laser1-light or --light-gray for the light and dark themes, respectively.


.todo-wrapper {
    margin-top: 60px;
    background-color: var(--todo-wrapper-bg);
    border-radius: 30px;
    margin-left: auto;
    margin-right: auto;
    padding: 30px;
}

Let’s check our work so far, but first, create four items in the ordered list using Emmet, remember? Inside the <ol> element, start typing li*4 and hit Enter.

SITK-ToDoList styling ToDo wrapper div

Now let’s style the only paragraph inside the to-do wrapper using the --completed variable we defined previously to set the colour; let’s set it up to italic and define margins. Please copy the code below to the end of the CSS file.


.todo-wrapper p {
    font-style: italic;
    color: var(--completed);
    margin: 5px 0;
}

The variable --completed will also be used to style the finished tasks’ font and colour.

Next, we will style the ordered list and its items, setting margin and some padding to the items to allow a little bit of space between them. Please copy the code below to the end of the CSS file.


.todo-wrapper ol {
    margin: 20px 0;
}

.todo-wrapper ol li {
    margin: 5px 0;
    padding: 3px;
}

Now let’s highlight the even items in our list for easy reading. Please copy the code below to the end of the CSS file.


.todo-wrapper ol li:nth-child(even) {
    background: #999999;
}

Let’s check our work so far.

SITK-ToDoList styling list items in the ordered list

Next, we will style the completed list items. First, let’s go to the HTML file and set the two first items as completed using class="completed" attribute.

SITK-ToDoList setting completed list items in the HTML file

Now, we will style those completed list items. Please copy the code below to the end of the CSS file.


.todo-wrapper .completed {
    text-decoration: line-through;
    color: var(--completed)
}
SITK-ToDoList styling completed list items

Time to style all buttons inside to-do wrapper div to look pretty. Remember that we used class="pretty-button" all buttons except the theme switcher? We are now changing their margin, padding, border-radius, background colour, border, text alignment, and more at once. Please copy the code below to the end of the CSS file.


.todo-wrapper .pretty-button {
    margin: 10px 2px;
    padding: 5px 20px;
    border-radius: 15px;
    background-color: var(--bg);
    border: none;
    color: var(--fontColor);
    text-align: center;
    display: inline-block;
    outline-style: none;
}
SITK-ToDoList styling all buttons inside To-do wrapper div

If you hover the mouse over the buttons, you will see no indication they are clickable. Let’s fix it by changing the cursor to a pointer and add a little bit of opacity as we hover over them. Please copy the code below to the end of the CSS file.


.todo-wrapper .pretty-button:hover {
    cursor: pointer;
    opacity: 0.75;
}

If we type in the text input field in the Form, we will notice the text look tight inside the text field. Let’s fix it by adding some padding. Please copy the code below to the end of the CSS file.


.todo-wrapper input {
    padding: 6px 6px;
}
SITK-ToDoList styling input text field

Next, we will fix the Add button, placing it on the input text field’s right side, which looks better.

SITK-ToDoList styling form children display

Display using inline-block makes the element generate a block box that’s laid out as if it were an inline box. According to QuirksMode: An inline-block is placed inline (i.e. on the same line as adjacent content), but it behaves as a block.

We will now align the buttons in the control-wrapper div using the display flexible box module, a relatively new and easy way to arrange elements on the page.  There is rich content here, so you can dig deep and learn more. For now, let’s say that the setup below finds a way to arrange the buttons at the center of the div and provides even spaces between them and the margins, which fits in most of the scenarios. Please copy the code below to the end of the CSS file.

#control-wrapper {
    text-align: center;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-around;
}

For the matter of responsiveness, the following code uses media query CSS resource to resize the .to-do wrapper width and .to-do wrapper input width. For devices with screen resolutions of at least 1400 pixels, the .to-do wrapper width will be set to 75%, and the .to-do wrapper input width will be 600 pixels. For devices with screen resolutions between 750 pixels and 1399 pixels, the .to-do wrapper width will be set to 50%, and the .to-do wrapper input width will be 400 pixels. Please copy the code below to the end of the CSS file.

@media only screen and (min-width: 1400px) {
    .todo-wrapper {
        width: 50%;
    }

    .todo-wrapper input {
        width: 600px;
    }
    
}

@media only screen and (max-width: 1399px) and (min-width: 750px) {
    .todo-wrapper {
        width: 75%;
    }

    .todo-wrapper input {
        width: 400px;
    }
}
SITK-ToDoList adding responsiveness

We can better see the responsiveness explained before in the following video where the .to-do wrapper width and .to-do wrapper input width resizes as soon as they reach the resolution limits defined in the media queries.

It is crucial to think of the behaviour you want your application to have in different devices and resolutions to catch and keep the user’s attention.

Let’s see the viewport metadata behaviour and how the elements arrange themselves in desktop, tablets, smartphones when properly styled using Flexbox, media queries, display inline-block and so on. Go back to the browser and click on the Toggle device toolbar button and play with the options.

Now that we have an awesome To-do List app well structured and styled, we will give it some action with JavaScript.

JavaScript

If you look at the page, you’ll see it has four buttons:

  • An Add button for adding new to-do items
  • A Clear Completed button for clearing items that you’ve marked as finished
  • An Empty List button for completely emptying the to-do list
  • A Save List button for saving what’s on the list

Of course, since you haven’t written any code yet, right now, none of them do anything!

Let’s add some code to our JavaScript file.

Add button

It is essential to tell JavaScript which parts of the HTML page are relevant and how user interactions with these parts should react. In this case, you want to tell it about the Add button and tell it to react when the user clicks this button.

Getting the Add button

Start by creating a button variable and telling JavaScript to get the id="add-btn" button element from the HTML document. Please type or copy the following code to the end of our JavaScript file and save it.

const addBtn = document.getElementById("add-btn");

Listening for the click

Now connect your button to an event handler so that JavaScript listens to a particular type of event and then runs a function when it hears it. The event is a click in our case. Do this with the function addEventListener. Please type or copy the following code to the end of our JavaScript file and save it.

addBtn.addEventListener("click", addToDoItem);

This listener will wait for the addBtn click, and when the click is heard, it will respond by running the addToDoItem function. Of course, because you haven’t written the addToDoItem function yet, it just won’t work yet!

Creating the function

Later in this post,  you will be writing code for your functions to add to-do items, clear the list, save it, etc. But for now, you want to check that you’ve connected your event listeners properly.

Create the addToDoItem function so that it will pop up an alert message informing the user which button they have clicked. Please type or copy the following code to the end of our JavaScript file and save it.


function addToDoItem() {
    alert("Add button clicked!");
}

Let’s check our work so far. Put the VS Code and the browser side by side again, and click on the Add button.

SITK-ToDoList checking Add button alert message

Write code for the remaining buttons.

Try it on your own, following the Add button steps to make sure you got the idea.

Connect the Clear Completed button, which has the Id clear-completed-btn, to an alerting function called emptyList.

Connect the Empty List button, which has the Id empty-btn, to an alerting function called clearCompletedToDoItems.

Connect the Save List button, which has the Id save-btn, to an alerting function called saveList.

Let’s see if you got it right! Check the code below and make some adjustments if needed or copy the following code to the end of our JavaScript file and save it.

let clearCompletedBtn = document.getElementById("clear-completed-btn");
clearCompletedBtn.addEventListener("click", clearCompletedToDoItems);
function clearCompletedToDoItems() {
    alert("Clear completed button clicked!");
}

let emptyBtn = document.getElementById("empty-btn");
emptyBtn.addEventListener("click", emptyList);
function emptyList() {
    alert("Empty button clicked!");
}
let saveBtn = document.getElementById("save-btn");
saveBtn.addEventListener("click", saveList);
function saveList() {
    alert("Save List button clicked!");
}

Don’t forget to save it! So far, the code should look like this.

SITK-ToDoList remaining buttons code so far

Click on all buttons to make sure they are working.

Add to-do items

Time to make the first of those buttons work correctly! This step will show you how to add a to-do item to the list.

First, go to the HTML file and delete all four list items <li> inside the ordered list <ol> and save it.

SITK-ToDoList clearing all list items from the ordered list

We are going to use a tiny bit of HTML in this step. Therefore you need to write some JavaScript to add <li> elements for each new to-do item. The user should enter text and then click the Add button to see it appear on the list as a numbered item.

First, just like you did with the buttons, create variables to select the text box and the ordered list. They already have the Ids todo-entry-box and todo-list. Please type or copy the following code to the end of our JavaScript file and save it.

let toDoEntryBox = document.getElementById("todo-entry-box");
let toDoList = document.getElementById("todo-list");

Now the text box and the ordered list can be easily reached from within your JavaScript.

Let’s create a function called newToDoItem to add an item to the list. This function will need to have two inputs:

  • The text of the item
  • The item completed or not

Of course, no new to-do item will ever be complete, but we are preparing ahead of time here: we will use the same function again when we load a saved list with some finished items. Please type or copy the following code to the end of our JavaScript file and save it.


function newToDoItem(itemText, completed) {
    let toDoItem = document.createElement("li");
    let toDoText = document.createTextNode(itemText);
    toDoItem.appendChild(toDoText);

    if (completed) {
        toDoItem.classList.add("completed");
    }

    toDoList.appendChild(toDoItem);
    toDoItem.addEventListener("dblclick", toggleToDoItemState);
}

This newToDoItem  does a few things.

let toDoItem = document.createElement("li");

Creates an li element to use as your new list item.

let toDoText = document.createTextNode(itemText);

Creates a text node, a special container for a text that we want to put inside an HTML element using JavaScript, and fills it with the contents of the itemText variable that is passed into the function.

toDoItem.appendChild(toDoText);

The function appendChild takes the element, or text node, that you pass to it (in this case toDoText), and puts it inside toDoItem. If there are already elements inside that one, the one we are adding now will be last.

if (completed) { toDoItem.classList.add("completed"); }

Checks if the value for the completed variable that was passed to newToDoItem is true. If it is true, it will add the class completed to the li element, changing how it looks on the page. In our CSS file, there are special styling rules text-decoration: line-through; for li elements.

toDoList.appendChild(toDoItem);

The function appendChild puts toDoItem, the <li> element inside of toDoList, the <ol> element.

toDoItem.addEventListener("dblclick", toggleToDoItemState);

Attaches an event listener for double-click to the toDoItem And tells it to call a function named toggleToDoItemState in response.

Next, connect to the function to the Add button. Change your existing addToDoItem function to get the text from the box and pass it to the function newToDoItem we’ve just created.


function addToDoItem() {
    let itemText = toDoEntryBox.value;
    newToDoItem(itemText, false);
}

Since a new to-do item is never finished, you can always pass false to the completed parameter of the newToDoItem function.

Let’s test it to see if it works!

SITK-ToDoList testing Add button

Completing items

When you can’t mark items as done, there’s not much point to a to-do list! It’s time to add that feature.

For a double-click on a to-do item, we have already set up the listener. All we need to do now is write a function that, when that double-click occurs, will toggle the item between complete and not complete.

Note that to mark items as complete, we are using the completed class. Not having that class implies that they aren’t complete. All our function needs to do is add or remove the class from the item’s class list, either add it if it is not yet on the list or delete it.

The this keyword

The trick is to know which item the class can turn on. You’ll need to use a new JavaScript keyword to describe the object that was clicked: this.

It’s a little bit complicated how exactly the this keyword works, but all we need to know here is that when used with a function called by an event listener, it means ‘the element to which the listener was bound.’ But you can use this to find the particular list item <li> we clicked on.

Add the toggleToDoItemState function to the end of our JavaScript like so:

function toggleToDoItemState() {
    this.classList.contains("completed") ? this.classList.remove("completed") : this.classList.add("completed");
}

Let’s test it to see if it works!

SITK-ToDoList marking items as completed

Remove items

If we have marked the items as complete, we will want to delete all of the completed items. Also, we may want to clear off everything on it if we come back to your list after a long time or if we just want to focus on something entirely different. To do this, we just need to update two functions that have already been connected to the buttons: clearCompletedToDoItems and emptyList.

Clearing completed items

Just as you can select all the elements in an HTML document, you can select the elements within any other element. The children of that element are called the elements inside another element. Likewise, just as you can select the Id elements, you can also select them by class.

Let’s update the existing clearCompletedToDoItems function with code to select the toDoList children who have the completed class to clear the completed items. Then loop over the selected items to remove one by one.

function clearCompletedToDoItems() {
    let completedItems = toDoList.getElementsByClassName("completed");

    while (completedItems.length > 0) {
        completedItems.item(0).remove();
    }
}

You will see that the code will always delete the item at position 0 of the list, the first item on the list. To do this, you need to use 0 since JavaScript starts counting at 0 and not 1. You delete this item to replace the first item every time the loop runs, so the list gets shorter and shorter. In this way, the loop will finally delete them all, no matter how many incomplete items are listed.

Let’s test it to see if it works!

SITK-ToDoList clearing items completed step1
SITK-ToDoList clearing items completed step2

Clearing everything

Let’s focus on the existing function emptyList. To delete all from the list, do the same thing as above, but pick all the children from the toDoList.


function emptyList() {
    let toDoItems = toDoList.children;
    while (toDoItems.length > 0) {
        toDoItems.item(0).remove();
    }
}

 It is always recommended to check your work as soon as you advance on it.

Save the list

You can save the to-do list to a local storage location on the user’s computer to make it even more useful. Then, as long as they open it in the same browser next time, it will remember their to-do list.

There are two important steps for this task: saving the list and loading it again when the page is reloaded if the list is there.

This gets a little tricky: HTML can’t be saved in the local storage, so we need to take the HTML code and convert it to pure JavaScript. To do that, you will need an array.

An array is a variable of a special kind, which is a list of variables. You can build one with square brackets and use the push method to add objects to it. You can remind yourself what a specific array item is, using alert and the item’s position in the array. Remember that JavaScript starts counting at 0. Take a look at the code below to understand it better.

const myArray = [];
myArray.push("array item 1");
myArray.push("array item 2");
alert(myArray[0]);
//This will alert "array item 1"

Next, we need to loop over the list toDoList and add each item to the array. Remember that we need to store the task and whether or not it has been completed. The best way to do this is to use JavaScript objects.

JavaScript objects

A JavaScript object is a set of properties and values. You create one like this:

const toDoInfo = {
    "task": "Learn HTML",
    "completed": false
};

You just need to save all the to-do items to local storage once you have converted them into objects. Local Storage can only store strings, but fortunately, JavaScript turns arrays into strings for you when you use the stringify function.

Saving in a Local Storage – Step 1

Enough said, let’s put it all together!

Let’s update the existing saveList function to:

  • Make an array
  • Use a for loop to put every item in toDoList into the array as an object
  • Stringify the array and store it in local storage with the key toDos

Here is the code for the saveList function.

function saveList() {
    const toDos = [];
    for (let i = 0; i < toDoList.children.length; i++) {
        let toDo = toDoList.children.item(i);
        let toDoInfo = {
            "task": toDo.innerText,
            "completed": toDo.classList.contains("completed")
        };
        toDos.push(toDoInfo);
    }
    localStorage.setItem("toDos", JSON.stringify(toDos));
}

Load the saved list – Step 2

We need to reverse everything we have done to save it, to load the list. But first, we need to check if anything needs to be loaded. You do this by checking whether there is no null value on the key you used to store the list. ‘Null’ is yet another ’empty’ word or ‘nothing’ word.

Let’s create a loadList function to:

  • Check if the toDos key exists in local storage
  • If it does, load it into a variable as an array
  • Loop over the array, and use newToDoItem to create new to-do items for everything in it

Add the code below to the end of our JavaScript file.

function loadList() {
    if (localStorage.getItem("toDos") != null) {
        let toDos = JSON.parse(localStorage.getItem("toDos"));

        for (let i = 0; i < toDos.length; i++) {
            let toDo = toDos[i];
            newToDoItem(toDo.task, toDo.completed);
        }
    }
}

Let’s call the loadList function after we have created it. Add the code below to the end of our JavaScript file and save it.

loadList();

To close it with a golden key, let’s save it to the local storage every time we add a new item, mark an item as completed, clear completed items, and empty the list.  All we need to do is adding the code below at the very end of addToDoItem, clearCompletedToDoItems, toggleToDoItemState and emptyList functions.

saveList();
SITK-ToDoList adding saveList to addToDoItem
SITK-ToDoList adding saveList to clearCompletedToDoItems
SITK-ToDoList adding saveList to toggleToDoItemState
SITK-ToDoList adding saveList to emptyList

Let’s test it to see if it works!

Congratulations, we have done a great job! I know it is a lot to take in, but you will get used to it with time and practice.

Let’s use Git to commit our work. We taught how to do it in our previous post; check it out here.

If you don’t know what Git is, take a look here and find out. The SITK-ToDo List source code can be found on our GitHub account.

We’ve got more of the basics of front-end web development. Certainly, there is much more to learn about HTML, CSS and JavaScript, and there are some free and excellent references for this: w3schools.com and MDN Web Docs.

If you need any help or something is going wrong, let me know. It will be my pleasure to help you. If you want to extend some point, we have discussed, contact me so we can cover in the next posts.

We also have pro bono projects just in case you are interested in learning more about them.

Be aware of why we started this project by clicking here.

Learn more about other posts here.

Contact us for any suggestions. And follow us on FacebookInstagram and Twitter.

If you are a good reader like myself, I recommend the following readings:

  • Docker Quick Start Guide: Learn Docker like a boss, and finally own your applications
  • Docker for Developers: Develop and run your application with Docker containers using DevOps tools for continuous delivery
  • Responsive Web Design with HTML5 and CSS: Develop future-proof responsive websites using the latest HTML5 and CSS techniques, 3rd Edition
  • JavaScript: The Definitive Guide: Master the World’s Most-Used Programming Language 7th Edition

See you in the next post!

Exciting Web Development Journey – The basics

Now that we have a cool development environment to brag about, why don’t we really use it? If you have no clue what I’m talking about, take a look at the series where we teach some concepts and prepare an exquisite, flexible, versatile software development environment with Docker.

My motivation

It is tough to please everyone when it comes to using the right tools, frameworks and technologies to become a good web developer, but we all concur that HTML, CSS and JavaScript are the nuts and bolts of web development. Therefore let’s get to know them and try to have fun on our journey!

HTML, CSS and Javascriptjs

I don’t want to reinvent the wheel; we all know there are many free interesting courses about this theme; there are many good CSS frameworks and JavaScript frameworks you should use or continue using. Nevertheless, during my long walk developing software, I can see many traps that people fall into due to a lack of understanding of those three guys’ basics. Still, if you stick with me, you will have a practical approach while learning a little bit of them.

What are HTML, CSS and JavaScript?

Those three are the foundational coding languages of the internet. They all work together to produce a web page. I would say that HTML adds meaning to raw content by marking it up, CSS formats that marked-up content and JavaScript is for making that content and formatting interactive, but it would sound boring, and most of the people newbie in this web development world wouldn’t still really understand or even get the big picture of it.

Therefore I always come up with the 3 friends analogy that I rescued from my childhood playing with Lego to make it clearer.

Let’s say that HTML is the first friend called The Builder responsible for the structure and content of a web page.

HTML - The builder

CSS is the second friend called The Artist, responsible for styling the web page’s content to give it a more appealing look and feel.

CSS - The artist

JavaScript is the third friend called The Wizard responsible for giving an interactive approach to the web page, allowing users to change the web page’s content and style providing such sensation that the page is alive.

JavaScript - The wizard

All of them, The Builder, The Artist and The Wizard, are essential for a web page to work seamlessly and beautifully.

For sure, I am pretty much talking about the Front-end Developer role kind of thing. We will cover some of the Backend Developer roles later, most likely in the next posts.

Let’s see how it works!

Scenario

Imagine that you are a front-end web developer. You are asked to cater to a wider variety of visitors on the Sharing Is The Key website, and you decide to incorporate support for light and dark themes keeping the brand identity. To demonstrate support for themes using CSS, you create a simple, proof-of-concept web page, and you write a JavaScript function to toggle the theme.

Learning goals

  • Develop a simple web page using HTML.
  • Style the page elements using CSS.
  • Define and create themes using CSS.
  • Add functionality for switching the theme using JavaScript.
  • Use browser developer tools to inspect the web page.

What you will need

  1. VS Code, the cross-platform code editor. Or one of your preference.
  2. Docker Desktop latest stable version. You can see how to set it up here.
  3. Familiarity with at least one programming language. You don’t need to be an expert.
  4. A computer running one of the following:
    • Windows: Windows 10 Home or Professional
    • Mac: macOS 10.9 or later
    • Linux: Ubuntu, Debian, Red Hat, Fedora, or SUSE

Project structure

Create a simple project structure that includes five files: an HTML file, a CSS file, a JavaScript file, a development container configuration file, and a Docker image file. You also add two VS Code extensions to simplify preview the web page in VSCode.


C:\SITK\WEBDEVJOURNEY\SITK-THEMES
¦   app.js
¦   index.html
¦   main.css
¦   
+---.devcontainer
        devcontainer.json
        Dockerfile

Let’s do this together!

If you prefer the CLI interface like myself, follow the Powershell or Windows Terminal section’s instructions below. If you prefer the VS Code interface, click here and follow the instructions under Create a new folder and Create some files sections.

Powershell or Windows Terminal

Getting to know Powershell or Windows Terminal if you use Windows or Shell if you use Linux is mighty, and you will need them sooner or later in your software development career. Therefore, I strongly recommend starting to use them as soon as possible. I prefer to use Windows Terminal, and from now on, I’ll call it “terminal” for short.

Open the terminal and run the following command:

New-Item -path "c:\sitk\WebDevJourney\SITK-themes\.devcontainer" -type directory

You should see something like this.

creating SITK-themes project folder structure

I created the SITK-themes project folder under the WebDevJourney folder just because this is the first of many projects in our Web Development Journey, but you can choose the folder you want.

Next, let’s create the HTML, CSS and JavaScript files under the SITK-themes folder running the command below.


New-Item -ItemType "file" -Path "c:\sitk\WebDevJourney\SITK-themes\index.html", `
                                "c:\sitk\WebDevJourney\SITK-themes\main.css", `
                                "c:\sitk\WebDevJourney\SITK-themes\app.js"

You should see something like this.

creating HTML CSS and JavaScript files under SITK-themes folder

Next, let’s create the Development Container configuration file and the Docker file that contains the Linux Alpine tiny distro.


New-Item -ItemType "file" -Path "c:\sitk\WebDevJourney\SITK-themes\.devcontainer\devcontainer.json", `
                                "c:\sitk\WebDevJourney\SITK-themes\.devcontainer\Dockerfile"
creating SITK-themes Development Container config file and Dockerfile

It is not really necessary to have this Development Container since we will not do anything complex here, but let’s get used to creating containers for all projects to keep our Software Development Environment well organized. Another remarkable advantage is that you will not need to install all software dependencies, VS Code extensions, and so on in the host to work with the project whether you format your machine later or work on it in another machine right away. If you are confused, take a look at a series of four posts where we depict this matter in more detail.

To make sure that you accomplished this task, run the following command.

Tree "C:\sitk\WebDevJourney\" /F | Select-Object -Skip 2

You should see something like this.

checking SITK-themes project tree

Now we have the SITK-themes project folder structure and files ready, let’s code!

In your terminal, make sure you are under the c:\sitk folder or in the folder you chose previously and run the following command.

cd .\WebDevJourney\SITK-themes\; code .

In your terminal, you should see something like this. Plus, it will open the VS Code in the SITK-themes project folder.

open VSCode via powershell command line

When VS Code is open, it will detect there is a Development Container configuration file devcontainer.json and will ask you to reopen the project into a Dev Container, but for now, ignore this notification because we don’t have anything in there yet.

SITK-themes project in VS Code

Press Ctrl + Shift + E to open VS Code Explorer to access the files or go to the menu View > Explorer.

Developing inside a Container

In your project, the devcontainer.json file tells VS Code how to access or create a development container with a well-defined runtime stack and tool. This container can run an application or run the required sandbox tools, libraries, or runtimes for a codebase to operate.

Workspace files are placed or copied, or cloned from the local file system into the container. Extensions are mounted and run within the container, where the tools, platform, and file system are completely accessible. This means that only by connecting to a separate container can you seamlessly switch your entire development environment. In summary, it is handy when you work on projects that require different development frameworks, libraries and platforms.

Copy the content below to the devcontainer.json file.


{
	"name": "SITK - Themes",
    "build": {
        "dockerfile": "Dockerfile",
    },
	"settings": {
		"terminal.integrated.shell.linux": "/bin/bash"
	},
	"extensions": [
		"eamodio.gitlens",
		"tht13.html-preview-vscode"
	],
}
JSON

JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language Standard ECMA-262 3rd Edition – December 1999. JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others. These properties make JSON an ideal data-interchange language.

From top to bottom, this JSON file contains the name of this Dev Container,  the docker file name, which contains the instructions for Docker to build the image for the container, the settings for an integrated Linux shell in the VS Code, and the two extensions you are going to need for this project.

Copy the content below to the Dockerfile file.


FROM alpine

RUN apk --update add git less openssh && \
rm -rf /var/lib/apt/lists/* && \
rm /var/cache/apk/*

As I said previously, we will use Alpine, one of the most lightweight Linux distribution, and that is all we need for this project. Always bear in mind that the lesser we need to accomplish a task, the better.

Let’s code HTML

Now let’s call our friend The Builder to start building our web page.

All HTML documents must start with a document type declaration: <!DOCTYPE html>.

The HTML document itself begins with <html> and ends with </html>.

The visible part of the HTML document is between <body> and </body>.

VS Code provides basic support for HTML programming out of the box. Syntax highlighting, intelligent completions with IntelliSense, and customizable formatting are available.

  • Open index.html by selecting the index.html file, type html:5, then hit Enter.

HTML5 template code is added to the file.

  • Edit your code so that it looks like the following, then save the file with Control + S or go to the menu File > Save.
html5 code template

The tag document type <!DOCTYPE html> indicates this is HTML5 code. If possible, always stick with the latest HTML version. Why? Well, because it is becoming a new standard, it is faster and reduces development time. It is modern because it works along with CSS3. It lets you do things previously impossible and supports mobile devices.

While we are not going to dig deeply into the definition of all the HTML elements, we will point out a few important stuff. The meta tag, which sits inside the head tag <head> shows metadata information that would not normally be available to the user unless the source code is shown in their browser. Meta elements or tags provide detailed and important information about the webpage. For instance, they help search engines display results to people.

The UTF-8 character set (charset) may seem trivial, but it is important for deciding how computers perceive characters. If the charset is missing, it might result in a security issue. Behind Charset, there is quite a bit of history and technical knowledge.

Viewport

HTML5 introduced a method to let front-end developers take control over the viewport through the <meta> tag. The viewport is the user’s visible area of a web page. You should include the following <meta> viewport element in all your web pages:

This gives the browser instructions on how to control the page’s dimensions and to scale.

The width=device-width part sets the page’s width to follow the device’s screen-width, which will vary depending on the device.

<meta name="viewport" content="width=device-width, initial-scale=1.0">

The initial-scale=1.0 part sets the initial zoom level when the browser first loads the page.

Head

The webpage’s title appears at the top of a browser window and is useful in a few ways. The title is used by and displayed in search engines, for instance. Let’s get a title added. Please change it to Sharing Is The Key, as shown below.

html page title

You can directly write the CSS code into the head of the web page, called internal CSS, to style the page’s HTML elements. It is best practice, however, to keep HTML structure and CSS styling apart.

It is called external CSS to have a distinct CSS file. When it is succinct and compartmentalized, the code appears to be simpler to read and understand.

You may use one or more external style sheets to serve several web pages. Instead of updating each HTML page with duplicated CSS, changes can be made once, and updates can be sent to all dependent pages.

  • In Visual Studio Code, add a blank line after the <title> element, type link and hit Enter.
  • Update the href to main.css and save the file with Control + S or go to the menu File > Save.
Link CSS to HTML

Body

  • Adding a heading <h1>, paragraph <p>and create a list item <li>.
  • Edit your code, or copy the code below and paste it between the opening <body> and closing </body> body tags.

    <h1>SITK - Themes</h1>
    <p id="tasks">Following Tasks:</p>
    <ul>
        <li class="list">Adding visual styles </li>
        <li class="list">Adding light and dark themes </li>
        <li>Switching the theme button </li>
    </ul>

An ID attribute id="tasks" used in the <p> will be used for styling this specific element as soon as we have code in the CSS file. The class attribute class="list" used in the <li> will be used for styling all elements of the same class.

Each list item <li> element is grouped into an unordered list <ul>.

Now that we have our simple web page properly structured, close VS Code and reopen it, but at this time, choose to reopen the project into the Dev Container when asked to do so.

Reopen SITK-themes project in a Dev Container

Click on Starting Dev Container (show log) as shown below.

VS Code Starting Dev Container show log

Please wait for VS Code to recognize a Dev Container configuration file, build the Alpine Linux container based on the image specified in the Dockerfile, set up the integrated Linux shell terminal, install the two VS Code extensions and be ready. The whole process takes one minute or so.

Once it is done, roll up the integrated terminal and see the HTML preview properly installed.

VS Code HTML preview extension

Now you have everything ready, resize the integrated terminal to see the whole HTML code and press Ctrl + Shift + V, which is the shortcut to the HTML preview, and here we go.

SITK-themes HTML page preview

To make it even better, grab the HTML preview and drag it to the right, as shown in the video below.

This is really handy when you want the coding to reflect immediately in the preview, as you can see below.

Sometimes things happen around us, and we don’t even notice. Please take a breath and realize that you are coding a simple HTML page into an Alpine Linux container with all you need to do so. So, congratulations on this accomplishment, but we are not done yet.

Many times you will need to troubleshooting to find some issues on the page. Let’s look at some useful developer tools in the browser where the page will actually run.

Developer tools

By opening the HTML file in a browser, you can preview your web page locally. Your browser points to the local file path instead of a website address that starts with https://. In my case, they will look like this: file:///C:/sitk/WebDevJourney/SITK-themes/index.html. If you saved the project in a different directory, don’t forget to change the path. Copy file:///C:/sitk/WebDevJourney/SITK-themes/index.html in your preferred browser. I’ll use Google Chrome.

You should see something like this.

SITK-themes running on Google Chrome
  • Open developer tools by pressing F12.
Developer tools - Google Chrome
  • Select the Elements tab
Developer tools - Google Chrome - Element tab
  • Rollover, select the HTML elements and open the disclosure triangles and explore them all.
Developer tools - Google Chrome - Element tab - exploring elements

In Developer Tools, the Elements tab shows you the DOM as rendered by the browser. It is also important to see when debugging how the browser interprets the source code.

DOM

DOM is a cross-platform and language-independent interface that treats an XML or HTML document as a tree structure wherein each node is an object representing a part of the document. The DOM represents a document with a logical tree.

Inspecting the page in a browser provides all sorts of useful information and can help you troubleshoot problems. You can also view CSS details with the inspector, as you’ll see soon.

It is not a big deal, but here is our first web page! It seems that although it is simple, we can put a little style to make it look better. Let’s call The Artist to decorate it.

Styling HTML with CSS

You may decide how your page should look by using CSS. The fundamental principle is to target HTML code and then define what the style should be.

Let’s apply CSS styles to page elements, add CSS code to support light and dark themes, and check the developer tools’ results.

External CSS

One advantage of external CSS is that several HTML pages can be connected to the same CSS file. If you make a change to the CSS, the style will be applied to each page. Designating an HTML file for page structure, a CSS file for styling, and a JavaScript file for event or interaction is called Separation of Concerns. We already have connected the CSS to HTML by adding this code <link rel="stylesheet" href="main.css"> to the index.html previously in this post.

CSS rules

Let’s imagine that The Builder built a house, and you’ve got the keys to all doors, and you are going decorate all of them. First, pick a door, then unlock it with the proper key. Once you have access to a room, you can decorate whatever you want. You could paint the walls blue, or you could set the floor to be hardwood. You pick a room and define the rules for how it should look. You may add the same style to multiple rooms. That is what CSS does.

In VSCode, open the main.css file and type this.


body {
    font-family: monospace;
}

ul {
    font-family: helvetica;
}

It is good practice to use CSS rules to style HTML. The unordered list element ul {} is a selector, which styles the <ul> HTML element. The declaration is font-family: helvetica and says what the font style should be. The property name is font-family , and the value is helvetica.

As you can see, the whole body font should be monospace, but only the Header1 element <h1> and the Paragraph element <p> were styled with this font. We use to say that the style cascaded from the body element to the Header1 and Paragraph elements because they are the body’s children and therefore inherit from their parent, but this only happens if no other style was defined for them. On the other hand, the Helvetica font took precedence over the monospace for the unordered list <ul> because it was explicitly defined.

CSS Rules

Selectors

ID and class selectors allow you to apply styles to custom attribute names in the HTML. An ID is used to style one element, and it should be unique, whereas classes can style multiple elements.

Type or copy the following code into your CSS file after the ul selector previously added.


li {
  list-style: circle;
}

.list {
  list-style: square;
}

#tasks {
  font-family: monospace;
}

The custom attributes are called .list and #tasks. The class selector list is prefixed by a period, while the ID selector is tasks and has a hashtag sign prefix. You choose their names as long as they match what you have defined in the HTML.

Save the changes and press Ctrl+Shift+V to open the HTML preview.

CSS Selector

Notice that all list items li were styled to have a circle for the bullets, but all list items with class="list" were defined to have squared bullets.

SITK light theme

Next, let’s add support for a colour theme for the SITK-themes web page. For the light theme, let’s use hex colour codes for the font colour (#0b0b0b, Cod Gray 1) and the background (#c8b568, Laser 1).

  • In the main.css file, add the following code at the end.
.light-theme {
color: #0b0b0b;
background: #c8b568;
}
  • In the index.html file, update the <body> element with a class name, light-theme, so the class selector for the light theme will apply the styles correctly.

<body class="light-theme">

apply light-theme to SITK-themes

View applied CSS using developer tools.

  • Copy file:///C:/sitk/WebDevJourney/SITK-themes/index.htmlin Google Chrome or Edge.
  • Open developer tools by pressing F12.
  • Select the Styles tab.
  • Select the Elements tab.
  • Rollover and select the HTML elements.
  • Select the <body> element. Notice the light-theme applied.
checking CSS applied in the developer tools
  • Open the disclosure triangles.
  • Select the <ul> element. Notice the custom style font-family: helvetica;, which overrides the style for the <body> element.
CSS overriding example

SITK dark theme

Now let’s set up plumbing for the dark theme in preparation for the last part in which we will allow theme switching on the web page.

  • Let’s add some constants to the page root in the main.css file.
:root {
--laser1: #c8b568;
--white: #ffffff;
--black: #0b0b0b;
}

The :root selector represents the <html> element in the HTML page. There are some situations where the best practice is to define a set of global CSS variables in the :root element. Here, we are defining three colour variables attached to the page root.

  • At the end of the main.css file, add the dark-theme selector and update the light-theme selector.

.light-theme {
--bg: var(--green);
--fontColor: var(--black);
}

.dark-theme {
--bg: var(--black);
--fontColor: var(--green);
}

In the code above, we defined some new variables, bg and fontColor, to specify the background colour and font colour. We used the var keyword to specify variables to use as the property values. We already set the values previously in the :root selector.

  • Next, in the main.css file, let’s add the following code after the :root selector, replacing the current body selector.
* {
color: var(--fontColor);
font-family: helvetica;
}

body {
background: var(--bg);
}

The * selector is a universal selector that applies to all page elements, except where a more specific element selector overrides it. Here, we use it to set the default color property for all page elements. For the color and background properties, you specify the variables defined in the light and dark theme selectors.

  • Remove the selector #tasks in the main.css, so that we can apply the same font to all elements.
  • To view the dark theme, manually let’s edit the default theme in the <body> element to dark-theme, and then press Ctrl+Shift+V to see HTML preview.
SITK-themes dark-theme preview
  • Let’s edit the <body> element to switch the default back to the light theme.

Javascript

JavaScript or ECMAScript is a programming language that allows you to add web page interactivity. Actually, JavaScript is the most commonly used programming language in the Software Development industry, according to Stack Overflow 2020 survey. It is nearly used by 70% of professional developers.

Stack Overflow is the largest, most trusted online community for developers to learn, share​ ​their programming ​knowledge, and build their careers for those who don’t know.

Now let’s call our friend The Wizard to give some functionality to our page!

When you click a button, JavaScript is the code that determines the event or action that will occur, such as opening a pop-up window. Using JavaScript, without reloading the web page, you can add or delete content like text. You can always use the browser to test and get feedback about your scripts as a web developer.

We will build a button to switch between light and dark themes and then wire the JavaScript code button to make the actual theme switch. When this is done, we will review our work using the developer tools.

Link HTML to JavaScript

Following the Separation of Concerns pattern recommendation, let’s use the separate JavaScript file previously created and link it to the index.html.

  • Right after the unordered list closing tag </ul> start a new line and type script:src, fill the double quotes with our JavaScript filename app.js  press Enter and save.
Link JavaScript to HTML

The script element could be placed in the <head> or elsewhere in the <body>. However, as best practice, putting <script> at the end of the <body>, allows all the page content to display on the screen first, then load the script after. This best practice will provide a better user experience when the project has huge JavaScripts to load on the page.

  • Let’s add fault tolerance so we can detect when a feature is not supported or available. After the script link you’ve just added, add a new line with the <noscript> element, which can be used to show a message if JavaScript is deactivated. Copy or type the code below and save the changes.

<noscript>You need to enable JavaScript to view the full site.</noscript>

Add JavaScript fault tolerance

Strict mode

When we start using JavaScript, our initial focus is often working with numbers, math, text manipulation, dates, and information storage. Sometimes JavaScript makes assumptions about the type of data we enter; assignment, math, or logical equality can give you unexpected results. Even if the result is an error, JavaScript tries to make the code run smoothly and provide you with a solution. To fix this behaviour, we can enable a strict mode that reduces silent errors, improves performance, provides more alerts, and reduces the number of unsafe features.

Let’s open app.js and type this code 'use strict'.

JavaScript strict mode

Toggle the theme button

We will need a button to switch themes.

  • In the index.html page, let’s add a button element <button>. Let’s put this at the end of the list inside of the <div> element. Copy or type this code below in a new line right after the unordered list closing tag </ul>.
<div>
  <button class="btn">Dark</button>
</div>
adding button to toggle the theme
  • In the main.css file, add a selector for the button. To make the button colours different from the general light or dark theme colours, set the color and background-color properties in the button selector. This selector, specific to the button, overrides the universal selector * used to apply font colours in our CSS file. Type or copy and paste the code below to the end of our CSS file.
.btn {
  color: var(--btnFontColor);
  background-color: var(--btnBg);
}
styling button to toggle theme
  • Next, let’s add some rules for the size, shape, appearance, and button absolute position. The following CSS creates a round button to the right of the page heading. Type or copy and paste the code below, replacing the existing button selector.
.btn {
  position: absolute;
  top: 20px;
  left: 250px;
  height: 50px;
  width: 50px;
  border-radius: 50%;
  border: none;
  color: var(--btnFontColor);
  background-color: var(--btnBg);
}
styling button to toggle theme
  • To improve the button’s appearance, let’s add a pseudo-class selector btn:focus after the button selector. By setting the outline-style rule to none, you eliminate a rectangular outline when the button is selected or receives focus. Type or copy and paste the code below to the end of our CSS file.
.btn:focus { 
    outline-style: none;
}
eliminating button outline when clicked or focused
  • Next, let’s update the CSS for the light and dark themes. Let’s define some new variables, btnBg and btnFontColor, to specify the button-specific background colour and font colour. Update the light-theme and dark-theme selectors with the code below.
.light-theme {
  --bg: var(--laser1);
  --fontColor: var(--black);
  --btnBg: var(--black);
  --btnFontColor: var(--white);
}

.dark-theme {
  --bg: var(--black);
  --fontColor: var(--laser1);
  --btnBg: var(--white);
  --btnFontColor: var(--black);
}
updating ligh-theme and dark-theme selectors

Event Handler

You need an event handler in your JavaScript file to make the button do something when you click it. Therefore we need to listen to the button click event. But, before you add the event handler, you will need a reference to this button.

  • In the app.js file, use document.querySelector to get the button reference. The document is a global variable representing the actual web page, and the querySelector method returns the first element that matches a specified CSS selector in the page.

const switcher = document.querySelector('.btn');

  • Let’s add the event listener and the event handler for the click event. In the following code, we are adding a listener for the click event. The function passed into the event listener is the actual event handler.
switcher.addEventListener('click', function() {
    document.body.classList.toggle('dark-theme');
});

We used the toggle method in the previous code to move the element to the dark-theme class. This will immediately add the dark theme styles instead of the light theme. To view the correct theme, however, the button label also needs to be changed, so you need to add a condition to verify the current theme and update the button label. Please add this command inside the event listener, right after the toggle command.

  let className = document.body.className;
  this.textContent = className == "light-theme" ? "Dark" : "Light";

In the code above, we created a variable className and assigned the body element class name to it, and we assigned a ternary operator result to this.textContent. The this is the switcher variable that points to the button element. In summary, this is the button. The conditional (ternary) operator is the only JavaScript operator that takes three operands: a condition followed by a question mark (?), then an expression to execute if the condition is truthy followed by a colon (:), and finally the expression to execute if the condition is false. This operator is frequently used as a shortcut for the if statement.

Press Ctrl+Shit+V to reload the HTML preview page.

adding JavaScript to the SITK-themes project

Now you can click on the button to contemplate our friend The Wizard‘s job done!! Congratulations, we made it; we built our first beautiful web page and gave it life.

Console message

Sometimes to better understand the page behaviour running on the browser, we can use console messages. It is a hidden message that will not appear on the web page but will show up in the browser developer tools.

In app.js, add a call to console.log after the ternary operator statement, but still inside the event listener.

  console.log('current class name: ' + className);
console logging in SITK-themes project

Check console messages in the browser.

  • Copy file:///C:/sitk/WebDevJourney/SITK-themes/index.htmlin Google Chrome or Edge.
  • Open developer tools by pressing F12.
  • Select the Styles tab.
  • Select the Elements tab.
SIT-themes project light-theme selected
  • Select the <body> element. In the Styles tab, look at the applied theme. If the current theme is dark, the dark-theme styles are applied.

Make sure the dark theme is selected.

SITK-themes project dark-theme selected
  • Select the Console tab to see the console.log message, “current <body> class name: light-theme dark-theme”.
SIT-themes project light-theme selected - check console log

Using the console log, we can better understand how the CSS theme switching is handled. Both class names are applied to the <body> element when we switch to the dark theme. However, the last class name applied, the dark theme, takes precedence. In the Styles tab, you can see that the dark theme rules override the light theme rules, shown using strikethrough text.

SIT-themes project light-theme selected - check console log details
  • To see the viewport metadata behaviour explained previously, which is very important to build a responsive web page that also fits mobile devices, click on the Toggle device toolbar button and open <head> HTML element.
SITK-themes project light-theme selected - mobile
  • Toggle to the dark theme.
SITK-themes project dark-theme selected -mobile
  • To understand the importance of the viewport metadata, comment the line 6 in index.html. Go to line 6 and press Ctrl+;  and save.
comment viewport metadata in SITK-themes project
  • Reload the page by pressing F5 to apply the browser’s changes and click on the Toggle device toolbar button to change to Desktop device and toggle back to mobile.
checking the results of commenting viewport metadata in SITK-themes project
  • Now, it’s time to save our work. Uncomment line 6 in index.html by pressing Ctrl+; again and save it.
  • Commit everything with the git extension we installed in our Docker Dev Container in the beginning. This way, you will have control of the source code and track all changes from now on. Go to Source Control extension by pressing Ctrl+Shift+G.
  • Click on the Initialize Repository button to create a new git repository for source control.
  • Type the message “Adding files” to the field and click on the commit button, as shown below.
git commit changes SITK-themes

It is always a good practice to commit all changes you make to your project frequently.

If you don’t know what Git is, take a look here and find out. The SITK-themes source code can be found on our GitHub account.

Congratulations!! We’ve got the basics of front-end web development. Certainly, there is much more to learn about HTML, CSS and JavaScript, and there are some free and excellent references for this: w3schools.com and MDN Web Docs.

If you need any help or something is going wrong, let me know. It will be my pleasure to help you. If you want to extend some point, we have discussed, tell me so we can cover in the next posts.

We also have pro bono projects just in case you are interested in learning more about them.

Be aware of why we started this project by clicking here.

Learn more about other posts here.

Contact us for any suggestions. And follow us on FacebookInstagram and Twitter.

If you are a good reader like myself, I recommend the following readings:

  • Docker Quick Start Guide: Learn Docker like a boss, and finally own your applications
  • Docker for Developers: Develop and run your application with Docker containers using DevOps tools for continuous delivery
  • Responsive Web Design with HTML5 and CSS: Develop future-proof responsive websites using the latest HTML5 and CSS techniques, 3rd Edition
  • JavaScript: The Definitive Guide: Master the World’s Most-Used Programming Language 7th Edition

See you in the next post!

xUnit, TestCafe, Docker and Jenkins - Sharing is the key

Software Testing Quality Assurance using xUnit, TestCafe along with Docker and Jenkins: an automated approach 3/3

For those who are following me, welcome back and thank you for your support! For those who are visiting for the first time, welcome and enjoy the ride!

Now we are going to close this series of three posts with a golden key. Catching up from our last post, when we saw how important CI/CD is to Software Development focused on Quality Assurance, we met Docker Compose as a useful alternative to a pure docker run command. We also discussed Jenkins and its Pipeline concept; we also saw how to extend Docker images. We also gave some best practices, hints and VSCode outstanding extensions to increase our productivity and avoid bugs. All those matters supported by a Software Testing Quality Assurance, Test-Driven Development overview and a little bit of talking about some important tools such as TestCafe we covered in the first post.

Jenkins Docker TestCafe

Provided you are more acquainted with Docker, Jenkins, TestCafe, we can push them a little bit further.  To automate the stages described in the Jenkins Workflow graph, we extended the Jenkins official Docker image by installing .NET Core SDK 3.1 in it. It is a good approach, but based on the Design Pattern Separation of Concerns, there is one more interesting than that. Why not let Jenkins image do what it does best without having any other necessary software component installed on it? What I am trying to say is, we don’t really need .NET Core SDK 3.1 installed in Jenkins image to restore, clean, build and unit test the eShopOnWeb solution. I will show you why and how right after this important discussion about Design Pattern.

Design Pattern – Separation of concerns

Separation of concerns is a pattern/principle of software architecture design for splitting an application into different parts, so each section addresses a separate concern. The ultimate purpose of the separation of concerns is to construct a well-organized structure. Each component fulfils a significant and intuitive function while optimizing its capacity to adapt to change.

The creation of boundaries achieves the separation of concerns in software architecture. Any logical or physical restriction that determines a given set of duties is a boundary. The use of methods, artifacts, modules, and services to describe core actions within an application would provide some examples of boundaries, tasks, solutions, folder hierarchies for source organization, application layers and organization processing levels.

Separation of concerns – advantages

  1. The lack of repetition and the uniqueness of the individual components’ function makes it easier to manage the overall structure.
  2. As a byproduct of improved maintainability, the system becomes more robust.
  3. The strategies needed to ensure that each element only concerns itself with a single set of coherent obligations often lead to natural points of extensibility.
  4. The decoupling that results from requiring components to concentrate on a single function leads to more easily reused components within the same system in other systems or different contexts.

Remember that, if not all, most of the well-known Design Patterns that we use in Software Engineering apply not only to coding as we know it but also to define the infrastructure and architecture to test and assure the quality of our application.

Going back to the point that I said you would not need .NET Core SDK 3.1 installed in the Jenkins image. The answer is simple: there is a way to run the restore, clean, build and functional, integration and unit tests of eShopOnWeb in a separate container, which can be called by Jenkins container. Furthermore, this container will exist only during the processing time of the task.

Enough theory, let’s make it happen!

We will use the Jenkins project we created in the previous post. Open it in VSCode; let’s create a directory called image2 in the Jenkins root directory and copy Dockerfile, build.sh, docker-compose.yaml, start.ps1 and stop.ps1 to this directory. We will make a few changes to them.

In build.sh we will change the name of the image to sitk/jenkins2 to preserve the original image as shown below.

sitk-jenkins2 image on build script

In docker-compose.yaml file, make the changes pointed in the image below.

changes to docker-compose

The change in the image and container_name is expected since we want to preserve the previous ones.

The change in the mapped port in the host side to 8081 is not to conflict with the one we set up in the previous post.

We added user: root and /var/run/docker.sock:/var/run/docker.sock to allow Jenkins container to access a remote Docker Server which resides in the Host machine to build images, start, stop, create and remove siblings containers. This involves mounting the host machine’s docker socket to the Jenkins container to start new sibling containers ( note, we are using the word siblings here instead of child containers because the newly created container will run alongside the Jenkins container rather than running inside the Jenkins container).

The reason to change the volume to jenkins-home-volume2 is because we don’t want to interfere in the existing one that is persisted in the host from the previous post.

In Dockerfile we removed .NET Core SDK 3.1 installation and included Docker Community Edition CLI installation since we will start siblings containers from within Jenkins container as stated above. To do this, copy the code below to Dockerfile which is inside image2 and save it.

FROM jenkins/jenkins:lts
 # Switching to root user to install .NET Core SDK
USER root

# Show the distro information during compilation!
RUN uname -a && cat /etc/*release

# Install docker
RUN apt-get update -qq \
    && apt-get install -qqy apt-transport-https ca-certificates curl gnupg2 software-properties-common 
RUN curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
RUN add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/debian \
   $(lsb_release -cs) \
   stable"
RUN apt-get update  -qq \
    && apt-get install docker-ce docker-ce-cli containerd.io -y
RUN usermod -aG docker jenkins    

# Switching back to the jenkins user.
USER jenkins

The start.ps1 and stop.ps1 remain the same.

Now let’s build the new image, go to Windows Terminal, open a bash terminal and under the /mnt/c/sitk/jenkins/image2 run ./build.sh. If everything went well, you should see something like this.

compiling sitk-jenkins2 image

Now we start a Jenkins container based on the newly created image by running start.ps1 in Windows Terminal under c:\sitk\jenkins\image2.

start new Jenkins container

Let’s access Jenkins at http://localhost:8081/ and set it up as we did in the second post. Notice that at this time, we will not need to install the .NET SDK Support plugin for Jenkins. Instead, we will need the Docker Pipeline plugin for Jenkins to dynamically build images, create, start, stop and remove containers in the Jenkins pipeline.

As soon as Jenkins is ready, go to Manage Jenkins -> Manage plugins, click on the Available tab, and in the search field, type “docker” and select Docker Pipeline and click on Install the Without Restart button.

Docker Pipeline plugin

Jenkins will also install the Docker Commons plugin since there is a dependency.

Making the most of the Jenkins plugins ecosystem, I want to present you Allure Test Report.

Allure Test Report

Allure Test Report is a flexible, lightweight multi-language test reporting tool. The test report tool demonstrates a succinct representation of what has been tested in a tidy web report form and helps those involved in the development process to gain maximum valuable knowledge from the regular execution of tests.

How it works

Allure is based on standard xUnit results output but adds some supplementary data. Any report is generated in two steps. In the first step during test execution, a small library called adapter attached to the testing framework saves information about executed tests to XML files. They already provide adapters for popular Java, PHP, Ruby, Python, Scala and C# test frameworks.

To install the Allure Report plugin, go to Manage Jenkins -> Manage plugins, click on the Available tab, and in the search field, type “allure” and select Allure and click on the Install Without Restart button.

Next, go to Manage Jenkins -> Global Tool configuration, scroll until you find the Allure Commandline section. In the Name field inside the Allure Commandline panel, type “Allure 2.13.7”. The version should match with the latest version you select in From Maven Central field as shown below.

setup allure in global tool configuration

We need to install the so-called adapter to the xUnit, the C# unit testing framework we are using on eShopOnWeb, as we mentioned in the first post.  For doing so, install XunitXml.TestLogger package to generate the XML files Allure will need to generate the reports.

Go to the Windows Terminal and under c:\sitk\eShopOnWeb run the following commands.

dotnet add ./tests/FunctionalTests/FunctionalTests.csproj package XunitXml.TestLogger --version 2.0.0
dotnet add ./tests/IntegrationTests/IntegrationTests.csproj package XunitXml.TestLogger --version 2.0.0
dotnet add ./tests/UnitTests/UnitTests.csproj package XunitXml.TestLogger --version 2.0.0

adding XunitXml-TestLogger

To run the Jenkins pipeline using the Docker and Allure plugins, we need a new Jenkinsfile. Therefore, create a new directory called Jenkins under the eShopOnWeb root directory and place a Jenkinsfile with the content below.

pipeline {
    agent none
    stages {
        stage('Checkout') {
            agent { 
                docker { image 'mcr.microsoft.com/dotnet/core/sdk:3.1-bionic'} 
            }
            steps {
                checkout([$class: 'GitSCM', branches: [
                    [name: '*/master']
                ],
                userRemoteConfigs: [
                    [url: 'https://github.com/sitknewnormal/eShopOnWeb.git']
                ]
                ])
            }
        }
        stage('Restore') {
            agent { 
                docker { image 'mcr.microsoft.com/dotnet/core/sdk:3.1-bionic'} 
            }
            steps {
                sh "dotnet restore --packages ./.nuget/packages eShopOnWeb.sln"
            }
        }
        stage('Clean') {
            agent { 
                docker { image 'mcr.microsoft.com/dotnet/core/sdk:3.1-bionic'} 
            }
            steps {
                sh "dotnet clean eShopOnWeb.sln"
            }
        }
        stage('Build') {
            agent { 
                docker { image 'mcr.microsoft.com/dotnet/core/sdk:3.1-bionic'} 
            }
            steps {
                sh "dotnet build eShopOnWeb.sln --no-restore --configuration Release"
            }
        }
        stage('Functional, integration and unit tests (xUnit)') {
            agent { 
                docker { image 'mcr.microsoft.com/dotnet/core/sdk:3.1-bionic'} 
            }
            steps {
                sh "dotnet test ./tests/FunctionalTests/FunctionalTests.csproj --configuration Release --logger xunit --no-build --no-restore --results-directory ./allure-results/FunctionalTests"
                sh "dotnet test ./tests/IntegrationTests/IntegrationTests.csproj --configuration Release --logger xunit --no-build --no-restore --results-directory ./allure-results/IntegrationTests"
                sh "dotnet test ./tests/UnitTests/UnitTests.csproj --configuration Release --logger xunit --no-build --no-restore --results-directory ./allure-results/UnitTests"
            }
        }
        stage('End 2 end eShopOnWeb tests with TestCafe') {
            agent { 
                docker { 
                    image 'testcafe/testcafe'
                    args '--entrypoint=\'\''
                } 
            }
            steps {
                sh "testcafe chromium:headless tests/e2eTests/*_test.js -r spec,xunit:allure-results/e2eTests/TestResults.xml" 
            }
        }
        stage('Publish Reports') {
            agent { 
                docker { image 'openjdk'} 
            }
            steps{
                script {
                    allure ([
                        includeProperties: false, 
                        jdk: '', 
                        results: [
                            [path: 'allure-results/FunctionalTests'],
                            [path: 'allure-results/IntegrationTests'],
                            [path: 'allure-results/UnitTests'],
                            [path: 'allure-results/e2eTests']
                        ]
                    ])
                }            
            }
        }
    }
}

It should look like this.

Jenkinsfile - pipeline with using docker and Allure

Don’t forget to rename the eShopOnWeb git URL if you forked or git cloned the project. You most probably have to change in this URL https://github.com/sitknewnormal/eShopOnWeb.git only the sitknewnornal highlighted in red your GitHub username. And most importantly, don’t forget to commit and push it to your repository because Jenkins will poll it from there when the pipeline runs.

There are some important changes I want to point out if we compare the previous Jenkinsfile with the new one.

comparing Jenkinsfiles

In the first Jenkinsfile on the left-hand side, we have all the pipeline stages running on any agent based on the previous post. It means that they will run on Jenkins master or one of the slaves’ containers if there were any. Since we know that there were no additional slaves in the Jenkins we set up previously, it will run in the Jenkins master container.

On the other hand, on the right-hand side, we defined one agent that will run using the official mcr.microsoft.com/dotnet/core/sdk:3.1-bionic for each stage in the pipeline. It means that Jenkins will pull the image from Docker Hub in the first run, create a sibling container that will run along with Jenkins, perform the step and then remove it. It will repeat this process for each stage.

If we focus on the Functional, integration and unit tests (xUnit) stage, we will notice that it will run in a container using the same image used in the previous stages. The logger highlighted in the image below will log the test results in the xunit format in the respective project directories under  ./allure-results directory so that the Jenkins Allure plugin can access and generate the report.

unit tests stage in jenkins pipeline depicted

For the End 2 end eShopOnWeb tests with TestCafe stage, we will run all 5 end-to-end tests in a container using testcafe/testcafe the official image. Notice that to use the testcafe chromium tests/e2eTests -r spec,xunit:allure-results/e2eTests/TestResults.xml command in the step, we needed to override the image entry point using the following argument --entrypoint=\'\' since, as we’ve seen in our previous post, the syntax is a little bit different. The parameter -r spec,xunit:allure-results/e2eTests/TestResults.xmltells TestCafe to report the test result to stdout using the spec argument and xunit the format file for the Allure use and place it in allure-results/e2eTests directory.

And finally, the last stage will publish the reports from all tests using the Jenkins Allure plugin. For doing so, it will run a container based on the OpenJDK official image. Notice that the mentioned plugin will use the results saved in the allure-results paths to generate the reports.

Now that we understood what the Jenkins Pipeline would do, let’s run it.

On Jenkins’s home page, select New Item.

Jenkins New Item

Enter eShopOnWeb as the item name, select Pipeline and click OK.

enter intem name select pipeline and click ok

We suggest you give it a brief description and click on the Pipeline tab.

basic cicd pipeline for eShopOnWeb solution

Select the Pipeline script from SCM option from the Definition field.

select pipeline script from SCM

Select the Git option from the SCM field.

select git from SCM field

Copy eShopOnWeb git URL https://github.com/sitknewnormal/eShopOnWeb.git to the Repository URL field, and make sure the Script Path field is “Jenkins/Jenkinsfile,” and click on the Save button.

Jenkinsfile script path

Before we run this Jenkins Pipeline, we need to run the eShopOnWeb application since there will be end-to-end tests with TestCafe.

For doing so,  make sure you have the MSSQL Server 2019 running. We explained how to do it here.

run eShopOnWeb whitout debugging

First, let’s run eShopOnWeb without debugging by selecting the menu Run and then Run Without Debugging as shown below and wait until it automatically opens the browser with the URL https://localhost:5001.

As soon as the application starts, go back to Jenkins and click on the Build Now button. It will take around 5min to finish the whole Pipeline, which includes the Allure Report publishing.

In the end, you will see something like this.

Jenkins pipeline eShopOnWeb result

If everything went well up to the Publish Report stage, click on the Allure Report icon, a colourful one that appears on the right side of the last execution on the Build History section, which In my case, is #3.

You will see the Allure Report showing that 79 out of 79 test cases have passed. It is not usual to have 100% of the test cases passed, but you will have a number that will help you to work on for sure.

Allure overview

There is much useful information in the Allure Report that you can get most of. It is not the purpose of this post to dig deep into Allure so, take time to explore Allure Report, and be positively surprised by how it can help you work on Software Quality Assurance.

Congratulations, we have made it! We went through some of the most important steps in the Software Development lifecycle using some powerful, cutting-edge tools and concepts to help us develop software better!! The Jenkins project and eShopOnWeb source code are on our GitHub account.

If you need any help or something is going wrong, let me know. It will be my pleasure to help you. If you want to extend some point, we have discussed, tell me so we can cover in the next posts.

We also have pro bono projects just in case you are interested in learning more about them.

Be aware of why we started this project by clicking here.

Learn more about other posts here.

Contact us for any suggestions. And follow us on FacebookInstagram and Twitter.

If you are a good reader like myself, I recommend the following readings:

  • Docker Quick Start Guide: Learn Docker like a boss, and finally own your applications
  • Docker for Developers: Develop and run your application with Docker containers using DevOps tools for continuous delivery
  • C# and .NET Core Test-Driven Development: Dive into TDD to create flexible, maintainable, and production-ready .NET Core applications, Kindle Edition
C# and .NET Core Test-Driven Development: Dive into TDD to create flexible, maintainable, and production-ready .NET Core applications, Kindle Edition
  • Modern Web Testing with TestCafe: Get to grips with end-to-end web testing with TestCafe and JavaScript 1st Edition, Kindle Edition
Modern Web Testing with TestCafe: Get to grips with end-to-end web testing with TestCafe and JavaScript 1st Edition, Kindle Edition
  • Continuous Delivery with Docker and Jenkins: Create secure applications by building complete CI/CD pipelines, 2nd Edition, Kindle Edition

See you in the next post!

xUnit, TestCafe, Docker and Jenkins - Sharing is the key

Software Testing Quality Assurance using xUnit, TestCafe along with Docker and Jenkins: an automated approach 2/3

This is the second of three posts about QA Testing using xUnit, TestCafe, Docker and let’s add Jenkins to give it an automated approach. For those who missed the previous post, it’s worth seeing it since most of the steps we are going to do here have some pre-requisites we covered there.

What is, what is used for and why Jenkins?

Jenkins logo

Jenkins is an open-source automation platform with lots of plugins designed for Continuous Integration or Continuous Delivery (CI/CD) purposes written in Java. Jenkins is used to constantly create and test software projects, making it easier for developers to incorporate project modifications and making it easier for users to acquire a new build. It also enables you to continuously deliver your applications by integrating with a wide range of testing and deployment technologies.

Continuous Integration / Continuous Delivery

Continuous Integration or Continuous Delivery is a software development practice in which developers are expected to commit changes many times a day or more regularly to the source code in a shared repository. Each commit made in the repository is then compiled. This helps the teams to identify the issues early. Besides this, many other roles depending on the CI/CD tool, such as deploying the build application on the test server, delivering the build and test results to the affected teams.

Besides all of that technical definition, Jenkins is this distinct gentleman in the image that came to serve you and make your life as a developer better. Let’s not take it for granted and make the most of Jenkins.

jenkins plugins

We all know that CI/CD is one of DevOps’ most important parts used to integrate various DevOps stages. For those who have no clue about what DevOps means, it is a set of practices integrating the development of software and IT activities. It aims to shorten the life cycle of system creation and provide high software quality for continuous integration and delivery.

With Jenkins, through automation, not only individual developers but also companies can accelerate software development. Jenkins incorporates all sorts of life-cycle development processes, including documenting, building, testing, packaging, staging, deployment, static analysis, and even more.

Jenkins’ benefits include:

  • With great community support, it is an open-source application.
  • It is straightforward to install using Jenkins docker’s official image.
  • To simplify your job, it has 1000+ plugins. You should code it and share it with the group if a plugin doesn’t exist.
  • It is cost-free.
  • It is Java-built and is, therefore, portable to all major platforms.

CI/CD with Jenkins depicted

Imagine a situation where the application’s full source code has been built and then deployed for testing on the test server. It sounds like the ideal way to build software, but there are several pitfalls in this process. I’m going to try to clarify them:

  • For the test results, developers have to wait until the full software is developed.
  • There is a strong probability that several bugs will be found in the test results. It is difficult for developers to find these bugs since they have to review the entire source code.
  • It delays the delivery process of applications.
  • There is a lack of continuous feedback on coding or architectural problems, build failures, test status and file release uploads, due to which software quality will decline.
  • The method as a whole is manual, raising the likelihood of frequent failure.

From the above-mentioned issues, it is evident that the software delivery process is sluggish, and software quality will likely deteriorate with time. This leads to disappointment with customers. So there is a desperate need for a system to exist to solve such frustration where developers would constantly activate a build and test for any change made in the source code. This is what CI/CD it’s all about. Jenkins is one of the most mature CI/CD platforms available, so let us see how Jenkins conquered the deficiencies in a simplified and self-explanatory workflow diagram below as an example.

simplified CI diagram with Jenkins

As you can see, Jenkins uses the pipeline concept to unleash the power of multiple steps according to parameters you set to perform both basic and complex tasks. When built, pipelines can build code and coordinate the work needed to push applications from commit to delivery.

Let’s make things happen now!

A useful hint I am going to give you is to use docker-compose as much as you can. Docker-compose is a tool for describing Docker multi-container applications and running them. With docker-compose, you use a YAML file to configure services for your application. Then, you build and start all the services from your configuration with a single command. Besides all of that, the YAML file is easy to read, and you will treat it as a source code and track all changes with your preferred SCM tool. Docker-compose is installed as part of the Docker Desktop. If you haven’t installed Docker Desktop yet, go to our first post of a series of four and follow the instructions.

docker-compose

Since I am using Windows, go to Powershell or Windows Terminal and create jenkins directory by running New-Item -Type Directory -Path jenkins command, as shown below.

Jenkins folder

From inside Jenkins directory type code . and hit Enter to open an instance of VSCode.

open vscode from iside jenkins folder

Create a file called docker-compose.yaml inside Jenkins folder.

docker-compose yaml file

Copy this code below into docker-compose.yaml file and save it.

version: '3.8'
services:
  cicd: 
    image: sitk/jenkins:lts
    container_name: sitk_jenkins
    ports:
      - 8080:8080
      - 50000:50000
    volumes:
      - jenkins-home-volume:/var/jenkins_home
volumes:
    jenkins-home-volume:
      driver: local
      driver_opts:
        type: none
        device: c:\sitk\jenkins\jenkins_home
        o: bind

It should look like this.

docker-compose yaml file filled up

YAML is a format for human-readable data serialization that can be used for all programming languages in combination and is also used to write configuration files. It is imperative to keep this file valid, which means that we need to follow some rules and indentations. Therefore I recommend using this popular VSCode extension with more than 4.6MM installs so it can help us to validate the file.

YAML Red Hat VSCode extension

We will use the official Jenkins Docker image jenkins/jenkins:lts and extend it installing .NET Core SDK since we will restore, clean, build and unit test the eShopOnWeb solution.

Docker Jenkins

Building a new Jenkins Docker image

Let’s build the sitk/jenkins:latest image. Create the image folder inside the Jenkins folder, and create a Dockerfile file inside the image folder with the content shown below.

FROM jenkins/jenkins:lts
 # Switching to root user to install .NET Core SDK
USER root

# Show the distro information during compilation!
RUN uname -a && cat /etc/*release

# Based on instructions at https://docs.microsoft.com/en-us/dotnet/core/install/linux-debian#debian-9-
# Installing with APT can be done with a few commands. Before you install .NET SDK, run the following commands 
# to add the Microsoft package signing key to your list of trusted keys and add the package repository.
RUN wget -O - https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.asc.gpg && \
    mv microsoft.asc.gpg /etc/apt/trusted.gpg.d/ && \
    wget https://packages.microsoft.com/config/debian/9/prod.list && \
    mv prod.list /etc/apt/sources.list.d/microsoft-prod.list && \
    chown root:root /etc/apt/trusted.gpg.d/microsoft.asc.gpg && \
    chown root:root /etc/apt/sources.list.d/microsoft-prod.list


# Install the .Net Core SDK, set the path, and show the version of core installed.
RUN apt-get update && \
    apt-get install apt-transport-https && \
    apt-get update && \
    apt-get install -y dotnet-sdk-3.1 && \
    export PATH=$PATH:$HOME/dotnet && \
    dotnet --version

# Switching back to the jenkins user.
USER jenkins

In the end, it should look like this.

sitk Jenkins Dockerfile

Now we will build the sitk/jenkins:latest with .NET Core SDK. Create a build.sh file into the image folder with this command: docker build -t sitk/jenkins . . It should look like this.

sitk Jenkins image build shell script

Go to Windows Terminal, open a Linux terminal. In my case, I have Ubuntu 20.04 running as Windows Subsystem Linux version 2. Go to /mnt/c/sitk/jenkins/image directory and run ./build.sh . If you missed it and want to catch up, go to our first post of a series of four.

After compiling the image, you should see something like this.

sitk jenkins image compilation

Now we are ready to run docker-compose. Go back to the Windows Terminal and type docker-compose up -d and hit Enter. I recommend saving the command in a start.ps1 file, so you don’t need to remember the syntax next time.

cicd service up and running

This command will start a container in detached -d mode with the service cicd which is based on the sitk/jenkins:lts image,…

docker-compose image

…it will name the container sitk_jenkins,…

docker-compose container name

…it will map the container inside ports 8080 and 50000, which are sitting at the right side of the colon, with the same ports at the host, which are sitting at the left side of the colon. Just for curiosity, port 50000 is used in robust environments when you need to attach some Jenkins Slave Agents to run pipelines in parallel. Mapping port 8080 to host is important so we can access the service via http://localhost:8080

docker-compose ports

…and the volumes declaration maps the /var/jenkins_home directory where all Jenkins data resides inside the container, to the c:\sitk\jenkins\jenkins_home in the host. This mapping is essential to manage it and attach it to another container for upgrades purposes, backup Jenkins data. Most importantly, it will also survive the container stop/restart/deletion, which means no Jenkins data loss.

docker-compose volumes

Go back to Windows Terminal, type docker ps -f "name=sitk_" and hit Enter. If everything went right, you would see Jenkins container running.

show Jenkins container running

We are almost there, now go to http://localhost:8080, you will see something like this.

Unlock Jenkins

Follow the instructions on the screen. Remember when we mapped the container /var/jenkins_home directory with the host directory c:\sitk\jenkins\jenkins_home in the YAML file? That’s another useful applicability of docker bind volumes. You don’t need to go inside the container to access files. Therefore, open c:\sitk\jenkins\jenkins_home\secret\initialAdminPassword file, copy the password and paste it to the administrator password field and click continue.

Click on the Install suggested plugins button.

Jenkins Install suggested plugins

And wait a couple of minutes for Jenkins to download and install all plugins.

Installing jenkins plugins.jpg

After the installation of the plugins, they will prompt you to create the First Admin User.

Jenkins first admin user

Type the information required and hit Save and Continue.

fulfill all required info for the Jenkins first admin user

Let the Jenkins URL unchanged (http://localhost:8080/) and hit Save and Continue.

Jenkins instance configuration

Now we are done with Jenkins’s initial setup.

Jenkins is ready

Click on Start using Jenkins button, and there you go!

Jekins login

Before we start creating pipelines for the suggested Jenkins Workflow graph, let me share with you an important hint. Create a Powershell script to stop and remove the Jenkins container with docker-compose rm --stop --force command, name it as stop.ps1, and save it in the same directory you saved the start.ps1 PowerShell script previously. Yes, you heard it right, the command will stop and remove the container and don’t be afraid of doing so because all we need for Jenkins to work next time we start it is in the c:\sitk\jenkins\jenkins_home directory, remember?

So, let’s do this. Go to Windows Terminal under c:\sitk\jenkins directory, type .\stop.ps1 to stop Jenkins’s container.

stop jenkins container

Let’s start it over again with the start Powershell script you previously created.

starting jenkins container

Let’s make sure that Jenkins is up and running because we will need it soon. Visit http://localhost:8080 and enter the admin user credentials you previously created and sign in.

Jekins login

Since we will check out from GitHub restore, clean, build and pack it, we will need .NET SDK Support plugin for Jenkins. Go to Manage Jenkins left panel option, click on Manage Plugins.

MAnage Jenkins manage plugins

Click on the Available tab, and in the search field, type “dotnet” and select  .NET SDK Support and click on the Install Without Restart button.

install NET SDK Support plugin

Please wait until it is completely installed.

NET SDK Support installed

Now we are ready to create the pipeline with some of the stages described in the Jenkins Workflow graph. Let’s use the eShopOnWeb, an ASP .Net Core application we used on posts three and four of our series of four posts. It is worth looking at them, but you can always visit our eShopOnWeb Github repository for more details.

On Jenkins’s home page, select New Item.

Jenkins New Item

Enter eShopOnWeb as the item name, select Pipeline and click OK.

enter intem name select pipeline and click ok

We suggest you give it a brief description and click on the Pipeline tab.

basic cicd pipeline for eShopOnWeb solution

Select the Pipeline script from SCM option from the Definition field.

select pipeline script from SCM

Select the Git option from the SCM field.

select git from SCM field

Copy eShopOnWeb git URL https://github.com/sitknewnormal/eShopOnWeb.git to the Repository URL field, and make sure the Script Path field is “Jenkinsfile,” and click on the Save button.

Copy eShopOnWeb git URL to the Repository URL field

Now we have to prepare Jenkinsfile because it will contain all stages that Jenkins will run in the pipeline. Therefore, go to the eShopOnWeb root folder, create the Jenkinsfile and copy the content below.

pipeline {
    agent any

    stages {
        stage('Checkout') {
            steps {
                git url: 'https://github.com/sitknewnormal/eShopOnWeb.git', branch: 'master'
            }
        }
        stage('Restore') {
            steps {
                sh "dotnet restore eShopOnWeb.sln"
            }
        }
        stage('Clean') {
            steps {
                sh "dotnet clean eShopOnWeb.sln"
            }
        }
        stage('Build') {
            steps {
                sh "dotnet build --configuration Release eShopOnWeb.sln"
            }
        }
        stage('Functional, integration and unit tests (xUnit)') {
            steps {
                sh "dotnet test eShopOnWeb.sln"
            }
        }
    }
}

It should look like this.

Jenkinsfile eShopOnWeb

Don’t forget to rename the eShopOnWeb git URL if you forked or git cloned the project. You most probably have to change in this URL “https://github.com/sitknewnormal/eShopOnWeb.git” only the sitknewnornal highlighted in red to your GitHub username. And most importantly, don’t forget to commit and push it to your repository because Jenkins will poll it from there when the pipeline runs.

To do so, I am using the popular GitLens VSCode extension, which I recommend.

GitLens VSCode extension

Select GitLens extension and stage Jenkinsfile file by clicking the plus (+) button.

Select GitLens extension and stage Jenkins file by clicking plus button

Type a commit description “first Jenkinsfile commit” and commit the changes by clicking on the checkmark button.

commit Jenkinsfile changes

Push all changes to the eShopOnWeb GitHub repository. Hit Ctrl+Ship+P to open Command Pallet, type “Push” and “Git: Push to…” option and select eShopOnWeb remote repository on the list. If you don’t have it click on “Add a new remote…”  and follow the instructions.

git push to eShopOnWeb GitHub repository

Now that we have everything set up, let’s run the Jenkins pipeline eShopOnWeb we’ve just created.

Go back to Jenkins in the pipeline eShopOnWeb and click on the Build Now button.

click build now button

You will wait a couple of minutes for the pipeline to execute, and you will see something like this.

pipeline eShopOnWeb result

As you can see, all stages in this pipeline have succeeded! The Jenkins project source code is on our GitHub account.

Congratulations, we’ve reached a relevant part of our goal. We now have one of the most powerful and popular CI/CD tools called Jenkins, taking care of our Software Development life cycle’s important steps.

We saved the end to end test with TestCafe and more for the next post because we don’t want to make this one very long. So let’s make a series of three instead of two as we planned before.

We also have pro bono projects just in case you are interested in learning more about them.

Be aware of why we started this project by clicking here.

Learn more about other posts here.

Contact us for any suggestions. And follow us on FacebookInstagram and Twitter.

If you are a good reader like myself, I recommend the following readings:

  • Docker Quick Start Guide: Learn Docker like a boss, and finally own your applications
  • Docker for Developers: Develop and run your application with Docker containers using DevOps tools for continuous delivery
  • C# and .NET Core Test-Driven Development: Dive into TDD to create flexible, maintainable, and production-ready .NET Core applications, Kindle Edition
C# and .NET Core Test-Driven Development: Dive into TDD to create flexible, maintainable, and production-ready .NET Core applications, Kindle Edition
  • Modern Web Testing with TestCafe: Get to grips with end-to-end web testing with TestCafe and JavaScript 1st Edition, Kindle Edition
Modern Web Testing with TestCafe: Get to grips with end-to-end web testing with TestCafe and JavaScript 1st Edition, Kindle Edition
  • Continuous Delivery with Docker and Jenkins: Create secure applications by building complete CI/CD pipelines, 2nd Edition, Kindle Edition

See you in the next post!

xUnit, TestCafe, Docker and Jenkins - Sharing is the key

Software Testing Quality Assurance using xUnit, TestCafe along with Docker and Jenkins: an automated approach 1/3

One of the most important steps in the Sofware Development life cycle is Software Testing Quality Assurance. There are many ways to deliver software with quality in all phases of its development process. It’s undeniable that the earlier you start introducing QA methods and techniques to your development process, the most cost-effective it will be. Furthermore, you will most likely finish it on time.

What is Software Testing Quality Assurance?

Software Testing Quality Assurance is a process to ensure the quality of software products or services offered by an organization to its customers. Quality assurance focuses on improving software production and making it effective and efficient in compliance with software products’ quality standards. Quality Assurance is commonly referred to as QA Testing.

It is usually based on Plan-Do-Check-Act steps, which are evaluated and improved periodically by the organization, most likely supported by tools.

To ensure that the product is designed and delivered with proper protocols, a company must use Quality Assurance. This allows, in the final product, to reduce problems and errors.

Quality Assurance best practices:

  • Create a robust testing environment
  • Select the release criteria carefully.
  • To save money, apply automated testing to high-risk areas. This helps to fasten the whole process.
  • Allocate appropriate time for each phase
  • Prioritizing bug fixes based on the use of apps is important.
  • Form a dedicated team for security and quality testing
  • Simulate tests in a production environment

There is much more to talk about Software Testing Quality Assurance, but it is not our focus on this post.

Software Testing – theory into practice

Lets’s use eShopOnWeb, a sample ASP.NET Core 3.1 reference application running on a docker container and persisting data in a SQL Server database for our Software Testing experiment. You can find more detailed info about preparing this environment here. It is strongly recommended since there are some pre-requisites steps we are not going to cover here.

Hit granularity test with xUnit

The xUnit is the collective name for several unit testing frameworks that derive their structure and functionality from Smalltalk’s SUnit. SUnit, designed by Kent Beck in 1998, was written in a highly structured object-oriented style, which lent easily to contemporary languages such as Java and C#.

xUnit testing

Some of the must-have best practices while developing software are building functional, integration and unit tests. In the image below, you will find out that eShopOnWeb BasketAddItem Unit Test uses xUnit, a free, open-source, community-focused unit testing tool for .NET Framework.

eShopOnWeb BasketAddItem unit test

To run all the eShopOnWeb solution tests, go to VSCode Terminal, which will lead to a bash terminal since the solution is running in a Linux container. Make sure you are in the solution root directory, which is /workspaces/eShopOnWeb$  and type dotnet test eShopOnWeb.sln and hit Enter.

running all eShopOnWeb solution tests

Developing into a container will give lots of advantages and huge flexibility if you happen to use different languages and or dependencies in your projects, not to mention that your workstation or laptop will not look like it is cluttered with many stuff installed. To learn more about this, visit our first post of a series of four.

Using TDD (Test Driven Development) approach in the development process is one of the most effective ways to develop functional, integration and unit tests. This is only a useful recommendation!

What is Test-Driven Development?

In software development, it’s a design process. It is based on a concise development cycle being replicated, and the criteria are converted into particular test cases.

These are TDD steps:

  1. Write a unit test that fails.
  2. Write enough code to make the test pass — at this step, we don’t care about good code.
  3. Refactor your code from the previous step.

We will not cover this technique here either, since the focus is to give an in-depth approach to Software Testing using xUnit, TestCafe along with Docker and Jenkins.

TestCafe: testing software from a user perspective

Even though building functional integration and unit tests are important for software testing for back-end and business tier, it is not enough when we are talking about web applications software quality assurance. We will most likely need to test the application in an end-to-end approach. Hence, we cover the frontend and its behaviour in different browsers, devices and screen resolutions simulating a test from a user perspective.

TestCafe logo

To run tests from a user perspective, there are many options, but let’s choose TestCafe, an open-source project which is a pure NodeJS end-to-end solution for web app testing. It takes care of all phases: starting browsers, running tests, collecting data from tests and producing reports. TestCafe doesn’t need browser plugins-it works out-of-the-box on all popular modern browsers.

The installation process for TestCafe is easy, but you might have problems getting stuff up and running locally. TestCafe depends on NodeJS to work. For example, you will need to set up at least one compatible browser on your device. In certain situations, for various purposes, such as having restricted access to your work machine’s user account, you might not be able to execute these instals easily.

There is also the scenario of running your end-to-end tests in various environments, such as continuous integration tools like Jenkins, for instance. The chances are that these systems would not have all the requisite dependencies you need to run your tests. Plus, you’ll need to ensure these systems remain up to date for each dependency.

docker

One of the key advantages of using Docker is that you can use the same image on several operating systems and not worry about anything working on each tester’s device differently. It ensures a consistent image in all environments with everything you need, provided you use the same image version.

Ok, enough of theory. Let’s materialized it. Considering you already have Docker installed in your machine and you have git cloned the eShopOnWeb, the steps we explained with details in the firstsecond and third posts of a series of four, we are now prepared to test some scenarios using the official and stable TestCafe Docker image.

First, let me explain the scenarios I have prepared. We are going to test 5 scenarios. TestCafe calls each scenario as a fixture:

  • eShopOnWeb Home page – verifies if the eShopOnWeb’s home page loads properly. Don’t forget to change the IP address of URL (https://192.168.2.217:5001/) shown below to your machine’s (i.e. the docker containers host)
eShopOnWeb home page load test scenario
  • eShopOnWeb Login – checks if the user can log in to their account
eShopOnWeb login test scenario
  • eShopOnWeb Use Case 01 – checks if the brand filter visual component  exists
eShopOnWeb use case 01 test scenario
  • eShopOnWeb Use Case 02 – checks if the brand filter is clickable and there is a “.NET” option
eShopOnWeb use case 02 test scenario
  • eShopOnWeb Use Case 03 – checks if the filter returned the right number of items