CSS made easy

Damien Riehl is a technology lawyer and musician. And he doesn’t agree with copyright infringement lawsuits, for him music is mathematics. And if you remember that there are only 8 musical notes, Damien had the brilliant idea of making an algorithm that made all the combinations and put it in the public domain. According to him, this can help in those cases where someone is prosecuted just because they used a combination used by another, and the second person didn’t even know. See the faq at http://allthemusic.info/ to understand better.

I’m not a lawyer, nor am I defending piracy, what caught my attention was the idea of noting that there is a finite space of possibilities. I think there is a fascination in being able to say ‘here’s everything about topic X’, personally that’s what attracted me to the master’s degree. I won’t be arrogant and say that I know everything about Design Thinking but I approached my studies with this objective.

Another topic: Think about the design of an HTML or SPA page, how many different ways are there to do it? We use CSS to control what is displayed. The colors are finite, ranging from #000000 to #FFFFFF; borders are top-bottom-left-right; and so on. Adam Wathan noticed this and developed TailwindCSS. With TailWind, you can write your CSS without leaving your HTML page. See the difference, before, if I wanted my text to be blue and bold, I would do this:

style.css

.info{
color: blue;
font-weight: bold;
}

index.html

<p class="info>
  Lorem Ipsum
</p>

And voilà, bold blue text. TailWindCSS allows you to write directly and it has a library with hundreds and hundreds of possibilities. As soon as you use it in your html, it automatically generates the CSS. This is a detail, an application runs in your terminal and scans your pages to know what to generate:

index.html

<p class="text-blue font-bold">
  Lorem Ipsum
</p>

or very large, bold, underlined, sky blue text centered on the page:

<p class="text-3xl font-bold underline text-sky-400 text-center">
  Lorem Ipsum
</p>

UnoCSS takes the idea further and eliminates the css file completely. In UnoCSS there is a script that analyses your pages at runtime and generates classes for you. And there is no need for an application running in the background. Magic! Note that the way of writing is exactly the same as TailWindCSS.

index.html

<p class="text-blue font-bold">
  Lorem Ipsum
</p>

I’ve been using UnoCSS a lot. But I have my criticisms, the large number of classes can make HTML code verbose, and it’s a fact, your HTML is difficult to understand. And the CSS generated by TailWindCSS is only readable by… TailWind developers 😀.

Additionally, the framework’s reliance on utility classes can lead to a lack of consistency in design across the site, as different people may use different classes to achieve similar effects. And since there is no CSS file maintained by the team, the need to document style choices is essential.

Still, the practicality seems to me to show that frameworks like these will be increasingly popular.

Cypress, new kid on the block

Selenium has been my go-to tool for UI test automation for a while, and for good reason. It was the front-runner, proving its worth time and time again. I’ve used it extensively but lately, I’ve leaned towards Cypress. I made a course on Cypress (all of the exercises can be found in my GH), I’ve become thoroughly convinced that it presents a more efficient and reliable alternative.

When it comes to analysing the differences between Selenium and Cypress, a few key factors stand out. Selenium supports multiple languages including Java, Python, C#, and others while Cypress is purely JavaScript. This could initially seem limiting but considering most web applications are now JavaScript-based, Cypress becomes a natural choice. Selenium tests run outside the browser while Cypress runs directly inside the browser. This lets Cypress take control of the entire automation process including network traffic, timers, and even the loading of JavaScript code. It’s this inner control that promises stability – a promise Selenium often can’t keep due to its reliance on numerous third-party factors. The possibility to test your automation with debugger options in the browser is game-changer, I spend a lot of time understanding my scripts using the console, styles and network tabs, I am sure FE developers are all very familiar with them.

Cypress provides a simple and comprehensive API to interact directly with the DOM, allowing you to write simple, effective tests, take a look:

describe('DOM Interactions Test', function() {
  it('Fills and submits a form', function() {
    cy.visit('https://www.example.com/form') // Cypress loads the webpage

    cy.get('input[name="firstName"]').type('John') // Cypress finds the input field firstName and types 'John' into it
    cy.get('input[name="lastName"]').type('Doe') // Cypress finds the input field lastName and types 'Doe' into it

    cy.get('button[type="submit"]').click() // Cypress clicks the submit button
  })
})

Compare this with Selenium

WebDriver driver = new ChromeDriver();

driver.get("https://www.example.com/form"); // Open URL 

WebElement firstName = driver.findElement(By.name("firstName"));
firstName.sendKeys("John"); // Fill the firstName input

WebElement lastName = driver.findElement(By.name("lastName"));
lastName.sendKeys("Doe"); // Fill the lastName input

WebElement submitButton = driver.findElement(By.tagName("button")); 
submitButton.submit(); // Submit the form

Cypress let you interact with CSS selectors!

Another key advantage of Cypress is its ability to handle asynchronous operations very easily.

describe('Asynchronous Handling Test', function() {
  it('Waits for an element to be visible', function() {
    cy.visit('https://www.example.com') // Cypress loads the webpage

    cy.get('#asyncButton', { timeout: 10000 }) // Cypress waits for the element with id 'asyncButton' to become visible for up to 10 seconds
      .should('be.visible') // Asserts that the element should be visible
      .click() // Clicks the button once it's visible
  })
})

Dealing with API’s is also a breeze:

describe("Todo List API Interaction", () => {
  // Define the base URL of your API
  const apiUrl = "https://example-api.com";

  beforeEach(() => {
    // Intercept the API call and load a JSON payload for the response
    cy.intercept("GET", `${apiUrl}/todos`, { fixture: "todos.json" }).as("getTodos");
    // Visit the application or a specific page where the API call is made
    cy.visit("/todo-list");
  });

  it("should fetch todos from the API and display them", () => {
    // Perform any actions that trigger the API call (e.g., click a button to fetch todos)
    cy.get("#fetch-todos-btn").click();

    // Wait for the API call to complete and the response to be displayed
    cy.wait("@getTodos");

    // Assert that the correct API endpoint was called and the response was handled properly
    cy.get(".todo-item").should("have.length", 3); // Assuming the response contains 3 todo items
  });
});

What to me is the big advantage is my caveat: interacting with CSS selectors demands that you be careful to choose those that are least likely to change and break your tests. Still, I only plan to use Cypress for my UI tests.