How to Add a CSS Gradient Overlay to a Background Image

This article was originally posted at https://medium.com/@christinatruong/how-to-add-a-css-gradient-overlay-to-a-background-image-170216435f65 and was kindly shared by Christina Truong. Check out more of her work at https://christinatruong.com.

Using images on a web page helps to add visual interest. But figuring out how to place text on top of the image, while making sure you can still read it, can be challenging. There are a few different ways to get around this issue.

UNLIMITED DOWNLOADS: 400,000+ Fonts & Design Assets

Starting at only $16.50 per month!



 

Prefer to watch a video? This article is a companion piece to my Decoded by Christina series on YouTube.

Option 1: Choose the right background image

The easiest option is to use images that have a big blank area for your text or doesn’t have a lot of detail.

Choose the right background image

If you don’t have your own photos, there are plenty of free and paid options for stock photography such as Adobe Stock or Unsplash.

Option 2: Edit the background image

Maybe there’s a specific photo that you want to use but it doesn’t have any blank areas for text or has a lot of detail. Another option is to use Photoshop or other image editing software to change the overall color profile of the image. This can help light colored text stand out by darkening the background image or vice versa.

To create this effect, add a black or white background color on a layer underneath the image, and then change the opacity of the image, to darken or lighten the photo.

edit the background image

Example of editing images in Photoshop by changing the opacity and adding a background layer

You could also add a color overlay or de-saturate the photo to make it black and white. Depending on the image, these techniques can help to make the text stand out.

Example of editing images in Photoshop by adding a color overlay and de-saturating the image

Example of editing images in Photoshop by adding a color overlay and de-saturating the image

This might be okay if you have a few images but if you’re using a lot of photos, it might get cumbersome to manually edit all the photos.

Accessibility

If you are making edits to images, it’s best to avoid adding text to the image itself. For the visually impaired, screen readers are used to read out the text in an HTML document. If the text is in the image, then it can’t be read aloud. And you want your web pages to be as accessible as possible.

While you can and should add an alt attribute to your images, this attribute is used for describing the image itself.

<img src="image.jpg" alt="description of image"/>

And most of the time, when working with text and images, the image file is added as a background image using the CSS background or background-image property, so you won’t even be able to add an alt attribute. So just avoid doing it altogether!

body {
  /* shorthand */
  background: url(image.jpg) no-repeat;
  /* longhand */
  background-image: url(image.jpg);
  background-repeat: no-repeat;
}

Option 3: Use CSS!

Luckily, there are ways to work with text and background images using different CSS properties and techniques.

The color and text-shadow property

Sometimes just adjusting the text color, with the color property, will do the trick. If the image is mostly dark, use white or a light color to add contrast, or vice versa if the image is mostly lighter colors.

In the example below, the heading is in an area of the image that has less detail, so a color change kind of works. But the paragraph text is still hard to read since it’s in a more detailed area of the image.

Screenshot from the Codepen containing the full HTML and CSS example

Screenshot from the Codepen containing the full HTML and CSS example

Another option is to add a drop shadow, using the text-shadow property.

h1, p {
  color: white;
  text-shadow: 1px 1px 3px #444;
}
Screenshot from the Codepen containing the full HTML and CSS example

Screenshot from the Codepen containing the full HTML and CSS example

But I find this doesn’t always work well with more detailed images. Plus it can be tricky to add a drop shadow to longer blocks of text or smaller font sizes.

To learn more about how to add a drop shadow to text, check out my text-shadow post.

The background property

Another option is to use the background property, which allows for a couple different ways to add color to either the the text or the image. The background property is actually shorthand for many different properties but this post will focus on background-color and background-image.

background-color

Let’s start with taking a look at how to use background-color to add a color to the element, which will display underneath the text. This property can be used with any type of <color> value.

background-color can be added to a single element, such as the h1 tag, in the Codepen example. It can also be added to a group of elements by selecting the containing element, such as the text-container div, which will add a color to the container being used to group all the paragraphs.

h1 {
  background-color: white;
}
.text-container {
  background-color: white;
}
another Codepen screenshot with text over background image

Screenshot from the Codepen containing the full HTML and CSS example

To have more of the background image showing through, add transparency by using an rgba() colour value instead of a keyword or hex code.

/* sets the color with alpha transparency */
background-color: rgba(70, 130, 180, 0.8);

The first three values specify the red, green and blue components, using numbers between 0 and 255, with each value separated by a comma. 0 represents black and 255 represents white. All other colors are a combination of the remaining numbers. The last value sets alpha channel, which is how the transparency is added. Any value between 0 and 1 can be used. 0 is fully transparent and 1 is solid. A percentage value can also be used. For example 0.8 is the same as 80%.

Another Codepen screenshot showing text over background image

Screenshot from the Codepen containing the full HTML and CSS example

You can find RGB values using image editing software like Photoshop or other online resources. Here are a couple that I like to use:

  • colours.neilorangepeel.com — browse through a bunch of different colors and find the RGB, keyword or hex values
  • rgb.to — if you already have a color value and just need to convert it to RGB, this tool will do that

This technique will only add a background color to the elements, not the entire background image. Also, text-based elements are usually block-level elements, so they span the full width of their container. Adding a background-color to it will add the color all the way across the container, even if the text doesn’t span all the way across—as you can see with the “heading” in the above example.

If you don’t want that effect, there are some workarounds using the display value. View the video example for more details.

background-image to create a color overlay

To change the look of the entire background image itself, use the background-image property to add a color overlay to the whole image instead. Here’s how it works:

  • Multiple images can be displayed using the background-image property
  • A gradient can also be displayed using the background-image property
  • Use a gradient and a background image to display a color overlay on top of the image

The Syntax

Multiple background images are separated with a comma. The images will be displayed stacked on top of each other. The first image listed will be on top of the following image.

background-image: url(image1.png); /* one image */
background-image: url(image1.png), url(image2.png); /* two images */

Gradient CSS data types are used to display a transition between two or more colors. Here’s the syntax using the linear-gradient function and an image. Make sure to define the gradient value first to create the color overlay effect, since the first value displays on top.

background-image: linear-gradient(color, color), url(image.png);

Here’s how this technique looks using a gradient on top of a background image:

background-image: linear-gradient(rgba(0,0,0 0.2),rgba(0,0,0,0.8)),
                  url(<https://picsum.photos/id/1031/500/400>);

I’ve added a black overlay, using the rgba() color value to add transparency, to be able to see the background image underneath. A gradient function also requires at least two color values: the first value is the top of the gradient, the second value is the bottom of the gradient. Also, because the values for this style can be kind of long, I like to put the two different values on its own line, just to make it easier to read. This is just my personal preference for formatting this type of declaration.

If you don’t want a gradient effect, then use the same values for both the top and bottom of the gradient. If you want to keep the gradient effect, just change the opacity levels or choose different colors.

Because the rgba() value requires parenthesis, which is contained within the linear-gradient() function which also uses parenthesis, it easy to accidentally to either be short a bracket or have too many. So just be sure to double check your syntax.

See the Pen
Working with text and background images
by Christina Truong (@christinatruong)
on CodePen.light

Now you can tweak and edit as you please without the need to make any changes to the image file itself. To see a more detail breakdown of the techniques mentioned in the article, check out the corresponding video.

 

Hi everyone, I’m Bonga_1

Hello everyone I am a c++ beginner programmer, currently doing finalising classes.
I wish to be assisted in fixing my code.
I want to pass an array of objects as an argument and then use that object to search for data in the program but
it seems like the objects are empty, please advise if me how do i fix such a bug.

Let’s Dive Into Cypress For End-to-End Testing

Software development without automated testing is hard to imagine today. A good variety of different test procedures will ensure a high level of quality. As a foundation for testing, we can use a number of unit tests. On top of that, in the middle of the pyramid, so to speak, are integration tests. End-to-end tests are at the very top, covering the most critical use cases. This third kind of testing will be the focus of this article.

However, end-to-end testing does have some pitfalls that are cause for concern:

  • End-to-end tests are slow and, thus, pose a significant hurdle in every continuous integration and continuous deployment (CI/CD) strategy. Not only that, but imagine finishing a task, a feature, or any other implementation — waiting for the test to execute can drain everyone’s patience.
  • Such end-to-end tests are hard to maintain, error-prone, and expensive in every way due to the effort of debugging. Various factors can cause this. Your test should feel like an assistant, never a hindrance.
  • The biggest nightmare for developers is a flaky test, which is a test that is executed the same way but leads to different results. It’s like a “Heisenbug”, which only occurs if you don’t measure the application being tested — that is, if you don’t look at it.

But don’t worry: You don’t have to succumb to these pitfalls. Let’s look at how to prevent many of them. However, I won’t just promise the moon and not deliver. In this guide, we’ll write some tests together, which I’ve made public for you in a GitHub repository. This way, I hope to show you that end-end testing can be fun! Let’s get started.

What Are End-to-End Tests?

When talking about end-to-end (or E2E) testing, I like to refer to it as being “workflow-based”. The phrase sums up end-to-end testing well: It simulates actual user workflows and should include as many functional areas and parts of the technology stack used in the application as possible. In the end, the computer is pretending to be a customer and tries to behave like a real user. These tests are best for applying constant stress to your application’s entire system and, thus, are a great measure to ensure quality when the whole application stack is present.

Let’s recall what we want to achieve with all of this. We know that front-end testing is a set of practices for testing the UI of a web application, including its functionality. Makes sense — with these measures, we can ensure that our application is working correctly and that no future changes will break our code. In order to achieve this efficiently, you might be wondering what and how much you need to test.

This is a valid question. You might find one possible answer in a metaphor: The test automation pyramid, first introduced by Mike Cohn and further specified by Martin Fowler, shows how to make testing efficient. We find fast and cheap unit tests on the lowest pyramid level, and time-consuming and expensive UI tests (end-to-end testing) at the top.

Explaining this and its advantages and drawbacks would be enough for its own article. I’d like to focus on one level. End-to-end tests in particular can bring significant improvements in quality if prioritized efficiently. In doing so, we can constantly put our system under stress and ensure that our application’s main functions are working correctly.

My Journey To Cypress

When I started learning how to write end-to-end tests, I used Mink, a PHP library, on top of Behat, a scenario-oriented behavior-driven development (BDD) framework. I started using Selenium, with all of its advantages and disadvantages. Because my team had started working with Vue.js a lot, we changed to a JavaScript-based testing framework to ensure flawless integration and compatibility. Our choice back then was Nightwatch.js, so I built our new test suite from scratch.

During this time, we often stumbled upon compatibility problems. You could call it dependency hell — not to mention all of the limitations we saw with Selenium and later with WebDriver.

  • On our team, we weren’t able to pin down the Chrome version of our CI. So if updates to Chrome were released, Nightwatch.js wasn’t fast enough to be compatible, causing many failures in our testing pipelines.
  • The number of test-sided causes of flaky tests started to rise, as the waiting possibilities of Nightwatch.js didn’t optimally match our product.

So, we came to consider building our test suite anew. After visiting an unconference, I discovered Cypress.

Cypress is an all-in-one testing framework that does not use Selenium or WebDriver. The tool uses Node.js to start a browser under special control. The tests in this framework are run at the browser level, not just remote-controlling. That offers several advantages.

In short, here are the reasons why I chose this framework:

  • Excellent debugging capability
    Cypress’ test runner can jump back to any state of the application via snapshots. So, we can directly see an error and all of the steps before it. In addition, there is full access to Chrome’s developer tools (DevTools), and clicks are fully recorded.
  • Better ways to wait for actions in the test or UI or in the responses from the API
    Cypress brings implicit waiting, so there is no need for appropriate checks. You can also make the test wait for animations and API responses.
  • Tests are written in JavaScript
    This mitigates the learning curve to write tests. Cypress’ test runner is open-source, so it fits our product strategy.

However, this article is a guide, so let’s stop with this general information and get going.

Getting Started

Install And Start Cypress

Let’s start from scratch. In my talks about Cypress, I usually begin by creating a new directory via mkdir, and then immediately installing Cypress. The easiest way to install is shown in this drawing:

A little hint: If you don’t want to use npm, you can install Cypress via Yarn:

yarn add cypress --dev

An alternative is the direct download, using the ZIP folders that Cypress provides. That’s it! Once the installation is complete, you’re ready to start.

There are two ways to start running Cypress tests. The first is by starting Cypress in the console, and running your tests headlessly:

./node_modules/.bin/cypress run

The second way is to use one of Cypress’ neat features, which is its integrated test runner. The test runner is a UI for running tests. To launch it, you can use a similar command:

./node_modules/.bin/cypress open

This command will open the test runner. When you open Cypress for the first time, you will see this interface:

Cypress provides some prewritten sample tests to showcase its features and give you some starting points — this is the reason for the tests that are available. Let’s ignore those for now, because we want to write our own soon. However, please keep this “Integration Tests” area in mind, because it will account for a lot of the magic that will happen later.

First Impression Of Cypress’ Structure

Now it’s time to open our newly created project in the integrated development environment (IDE) of choice. If you navigate to this folder, you will see the following test structure:

smashing-example
  └── cypress
      └── fixtures
      └── integration
      └── plugins
      └── support
  └── cypress.json

Let’s go over these folders:

  • fixtures
    Here is where you’ll find fixed test data, which have no relation to the other entities. So, no IDs are stored here, which can change according to the local state.
  • integration
    You will find the actual tests here.
  • plugins
    Here, you can extend Cypress, whether with existing Cypress plugins or your own.
  • support
    Here, you can extend Cypress itself. Your own commands and helpers are located here.
  • cypress.json
    Modify configurations here, including for the environment.

All right, I think we can find our way around Cypress now, whether the test runner or the source code. But how do we start? What do we want to test?

Choose A Test Case

A typical end-to-end test can get complex, particularly if has a lot of steps. It would take a lot of time to execute manually. Because of this complexity, E2E tests can be challenging to automate and slow to run. As a result, we need to carefully decide which cases to automate.

In my opinion, the term “workflow-based” is key: We would select test cases based on typical user stories. However, due to run times, it is not advisable to cover every single available workflow. Therefore, we need a way to prioritize our test cases.

On my team, we had several criteria for our project. The test case should:

  • cover the most general and most used workflows of a feature, such as CRUD operations (the term “happy path” describes these workflows quite well);
  • use risk analysis, covering the workflows with E2E tests that are most vulnerable (i.e. where errors would cause the most damage);
  • avoid duplicate coverage;
  • not necessarily be used if unit tests are more appropriate (use an E2E test to test your software’s response to an error, not the error itself).

The second most important thing to keep in mind is to only test the workflow that you explicitly want to test. All other steps required to make your test work should be done with API operations outside of the test, to avoid testing them. This way, you will ensure minimal test run times and get a clear result of your test case if it fails. Think of this workflow as an end user would: Focus on using the feature rather than on the technical implementation.

Example:

If you want to test the checkout process in an online shop, don’t perform all of the other steps, such as creating the products and categories, even though you will need them to process the checkout. Use, for example, an API or a database dump to make these things, and configure the test only for the checkout.

Example: Finding My Articles in Smashing Magazine

I want to write a test for this website, Smashing Magazine. I cannot guarantee that this test will be up to date forever, but let’s hope it will last. Either way, you’ll be able to find this example in a GitHub repository.

Creating Our First Cypress Test

In the integration folder, we’ll begin by creating a new file. Let’s call it find-author.spec.js. The suffix .spec stands for “specification”. In terms of a test, this refers to the technical details of a given feature or application that your application must fulfill.

To turn this empty JavaScript file into a test’s home, we’ll start by giving the test suite its structure. We’ll use the method called describe. describe(), or context(), is used to contain and organize the tests. In other words, this method serves as a frame for our tests. Thus, our test file will look like this:

// find-author.spec.js
describe('Find authors at smashing', () => {
  //...
});

The next step is to create the actual test. We’ll use the method it. it(), or specify(), is used to represent the actual test. As you can see, we can capture multiple tests in one file, which allows for some excellent structuring options.

// find-author.spec.js
describe('Find authors at smashing', () => {
  it('Find the author Ramona Schwering', () => {
    cy.log('This is our brand-new test');
  });
});

Little hint: If you’re familiar with Mocha, you might have noticed some similarities. Cypress is built on top of Mocha, so the syntax is the same.

All right, let’s proceed. If we run our test in Cypress’ test runner, we’ll notice that Cypress will open a browser to run the test. This browser is seen in the screenshot below:

Congratulations! We’ve written our first test! Sure, it doesn’t do much. We need to continue. Let’s fill our test with life.

Fill The Test With Life

What’s the first thing to do when testing a website? Right, we need to open the website. We can do that using a Cypress command. What is the command, you might be wondering?

Working With Commands

There are mainly two types of instructions used in an E2E test. The first type of instruction, the commands, represents the individual steps in the test. In the context of Cypress, commands are everything that Cypress does to interact with your website. This interaction could be anything — a click, scrolling down the website, or even finding an element. As a result, commands will be one of the important things we’ll fill our test with.

So, our first command will be the one to navigate to the website — smashingmagazine.com. This command is called visit.

Using it, our test will look like this:

// find-author.spec.js
describe('Find authors at smashing', () => {
  it('Find the author Ramona Schwering', () => {
    cy.visit('https://www.smashingmagazine.com/');
  });
});

There is one command that I use often — and you will, too. It’s called get:

cy.get(‘selector’);

This command returns an element according to its selector — similar to jQuery’s $(…). So, you would use this command to find the parts to interact with. Usually, you would use it to start a chain of commands. But wait — what is meant by a chain of commands?

As mentioned at the beginning of this article, all tests and everything else that goes with them are written in JavaScript. You can put the commands in the tests (i.e. the statements) in a chain (chained, in other words). This means that the commands can pass on a subject (or return value) of a command to the following command, as we know from many test frameworks.

All right, we will start a chain of commands with the get command. To find an element with get, we need to find its selector first. Finding a unique selector is essential, because Cypress would otherwise return all matching elements; so, keep this in mind and avoid it if it’s unintended.

Interacting With Elements

Cypress itself has a feature to help you find the selectors of the elements that you want to work with. This feature is called the Selector Playground, and it helps you to discover unique selectors of a component or to see all matching elements for a selector or a text string. So, this feature can help you a lot in this task. To enable it, simply click the crosshair icon in the header of your test’s UI, and then hover over the desired element:

As seen in the screenshot above, a tooltip will display the selector on hover or in this little bar under the crosshair icon, which appeared when the element was clicked. In this bar, you can also see how many elements would match the given selector — ensuring its uniqueness in our case.

Sometimes, those automatically generated selectors might not be the ones you want to use (e.g. if they are long or hard to read or do not fulfill your other criteria). The selector generated below is challenging to understand and too long, in my humble opinion:

In this case, I would fall back to the browser’s DevTools to find my unique selectors. You might be familiar with these tools; in my case, I often choose Chrome for this purpose. However, other supported browsers might provide similar features. The process feels similar to the Selector Playground, except that we’re using the DevTools’ features in the “Element” tab.

To ensure that a selector is unique, I’d recommend searching for it in your DevTools’ code view. If you find only one result, you can be confident that it’s unique.

Did you know that there are many different selector types? Depending on the variety, tests can look and even behave pretty differently. Some varieties are better suited to end-to-end testing than others. If you want to know which selectors to use to keep your tests stable and clean, I can point you to one of my articles that covers this issue. Cypress’ developers themselves provide some guidance on this topic in their best practices.

Our Test As A Sequence Of Commands

OK, back to our test. In it, we want to display our workflow:

“I, as a user, will search for the author’s article and navigate to the author’s website through the reference area in one of their articles.”

We’ll reproduce the steps that a user would take by using commands. I’ll paste below the finished test with comments, which will explain the steps:

// find-author.spec.js
it('Find the author Ramona Schwering', () => {
  // Open the website
  cy.visit('https://www.smashingmagazine.com');

  // Enter author’s name in search field
  cy.get('#js-search-input').type('Ramona Schwering');

  // Navigate to author’s article
  cy.get('h2 > a').first().click();

  // Open the author’s page
  cy.get('.author-post__author-title').click();
});

This example deals with the workflow that we want to test. Cypress will execute this test. So, is it time to say “Congratulations”? Have we finally finished writing our first test?

Well, please take a closer look. Cypress will execute it, but it will only do what the test tells it to, which is whatever you wrote. If you run it in the test runner, you can see whether it has passed — but not in case you ran it headlessly. With this test, we only know whether Cypress could run our commands successfully — not whether we ended up on the author’s website. So, we need to teach our test to determine that.

Working With Assertions

The second type of statement takes care of the descriptions of the desired state of the UI — that is, whether something should exist, be visible, or no longer be visible. The assertions in Cypress are based on Chai and Sinon-Chai assertions, which is noticeable in the syntax.

Remember that we want to check whether we’re on the author’s profile page — mine in this example. So, we need to add an assertion for exactly that:

// find-author.spec.js
it('Find the author Ramona Schwering', () => {
  // Open the website
  cy.visit('https://www.smashingmagazine.com');

  // Enter author’s name in search field
  cy.get('#js-search-input').type('Ramona Schwering');

  // Navigate to author’s article
  cy.get('h2 > a').first().click();

  // Open the author’s page
  cy.get('.author-post__author-title').click();

  // Check if we’re on the author’s site
  cy.contains('.author__title', 'Ramona Schwering').should('be.visible');
});

All right, now we’ve written a test that has value. So, yes, congratulations on writing your first test… even if it’s not yet perfect.

Let’s Make Our Test Pretty

Even if we’ve succeeded in writing a first meaningful test and learned the core concept in the process, I wouldn’t merge this one yet if it was proposed in a pull request. A couple of things are left to do to make it shine.

Take Your Time

Cypress has a built-in retry option in almost every command, so you don’t have to wait to see whether, for example, an element already exists. However, this only looks to see whether an element exists in the DOM, not more than that. Cypress can’t predict everything your application does, so there might be some flakiness if you rely solely on this.

What would a user do if they wanted to see a website that is still loading? They would most likely wait until some parts of the website become visible (thus, loaded) and would then interact with them. In our test, we want to mimic precisely that: We want to wait for changes in the UI before starting to interact. In most cases, we’d limit this behavior to the elements we need, thus using assertions on those elements.

As you can see, we must make our test wait on several occasions. However, waiting too many times is not good either. As a rule of thumb, I’d suggest using an assertion to check whether the element to be interacted with has fully loaded, as the first step to determining whether the website being test has loaded.

Let’s take a look at such a part of our test as an example. I added one assertion to make sure our page has fully loaded:

// find-author-assertions.spec.js
// Open website
cy.visit('https://www.smashingmagazine.com');

// Ensure site is fully loaded
cy.get('.headline-content').should('be.visible');

// Enter author’s name in the search field
cy.get('#js-search-input').type('Ramona Schwering');

Keep adding assertions in such a manner to all instances where our website will have loading times or several elements that need to be rendered anew. For the complete test file, please look at the corresponding test in the GitHub repository.

To avoid falling into the trap of flaky tests, I would like to give you one last hint: Never use fixed wait times, such as cy.wait(500) or the like.

API Responses Are Your Friends

There’s one neat waiting possibility in particular that I love to make use of in my tests. In Cypress, it’s possible to work with network features — another helpful way of waiting in your application is to use these features to work with network requests. This way, you can make the test wait for a successful API response.

If we remember our workflow as an example, one step could make great use of an API waiting possibility. I’m thinking about search. A corresponding user story could be the following:

“I, as a developer, want to make sure that our search results have fully loaded so that no article of older results will mislead our test.”

Let’s apply that to our test. First of all, we need to define the route that we want to wait for later on. We can use the intercept command for this. I would search for the request, bringing the data that I need — the search results in this case.

To keep this example simple, I’ll use a wildcard for the URL. After that, I’ll use an alias so that Cypress can work with this route later on.

// find-author-hooks.spec.js
// Set the route to work with
it('Find the author Ramona Schwering', () => {

  // Route to wait for later
  cy.intercept({
    url: '*/indexes/smashingmagazine/*',
    method: 'POST'
  }).as('search'); // With this alias Cypress will find the request again

  //...

In Cypress, all defined routes are displayed at the beginning of the test. So, I’d like to put those intercept commands at the beginning of my test, too.

Now, we can use this route alias in assertions. The leanest way to do this would be with Cypress’ wait command, directly with the alias mentioned before. However, using this command alone would lead to waiting for the response regardless of its outcome. Even error codes such as 400 or 500 would count as passing, whereas your application would most likely break. So I’d recommend adding another assertion like this:

// find-author-hooks.spec.js
// Later: Assertion of the search request’s status code
cy.wait('@search')
   .its('response.statusCode').should('equal', 200);

This way, we can wait for the software’s data, changes, and so on with precision, without wasting time or getting into problems if the application is heavily stressed. Again, you can find the complete example file in my GitHub repository.

Configuring Cypress

I’ve left out one small detail. If you take a closer look at the complete test example, it differs slightly from those we used here in this guide.

// Cypress
describe('Find author at smashing', () => {
  beforeEach(() => {
    // Open website
    cy.visit('https://www.smashingmagazine.com');
  });

//...

I only use a slash to open the website of Smashing Magazine. How does that work? Well, using this command like so will navigate to the baseUrl of our tests. baseUrl is a configuration value that can be used as prefix for the cy.visit() or cy.request() command’s URL. Among other values, we can define this value in the cypress.json file. For our test, we’ll set the baseUrl like so:

// cypress.json
{
  "baseUrl": "http://www.smashingmagazine.com"
}

Honorable Mention: Hooks

There’s one topic left that I want to mention, even if our example test is not suited to using it. As is common in other test frameworks, we can define what happens before and after our tests via so-called lifecycle hooks. More precisely, these exist to execute code before or after one or all tests:

// Cypress
describe('Hooks', function() {
  before(() =>  {
    // Runs once before all tests
  });

  after(() =>  {
    // Runs once after all tests
  });

  beforeEach(() =>  {
    // Runs before each test
  });

  afterEach(() => {
    // Runs after each test
  });
});

We want to fill our test file with more than one test, so we should look for common steps that we want to execute before or after them. Our first line is a case in point, being the visit command. Assuming we want to open this website before each of these tests, a beforeEach hook in our example would look like this:

// Cypress
describe('Find author at smashing', () => {
  beforeEach(() => {
    // Open website
    cy.visit('https://www.smashingmagazine.com');
  });

  //...

I frequently use this in my daily work to ensure, for example, that my application is reset to its default state before the test, thus isolating the test from other tests. (Never rely on previous tests!) Run your tests in isolation from each other to maintain control over the application’s state.

Each test should be able to run on its own — independent of other tests. This is critical to ensuring valid test results. For details on this, see the section “Data We Used to Share” in one of my recent articles. For now, refer to the complete example on GitHub if you want to see the entire test.

Conclusion

In my opinion, end-to-end tests are an essential component of CI, keeping the quality of applications at a high level and at the same time relieving the work of testers. Cypress is my tool of choice for debugging end-to-end tests quickly, stably, and efficiently, and for running them parallel to any pull request as part of CI. The learning curve is gentle if you’re already familiar with JavaScript.

I hope I’ve been able to guide you a bit and given you a starting point to write Cypress tests and some practical tips to get started. Of course, all code examples are available in the GitHub repository, so feel free to take a look.

Of course, this is only a starting point; there are many more things to learn and discuss regarding Cypress tests — I’ll leave you with some suggestions on what to learn next. With this in mind, happy testing!

Resources

Beginners Guide to Installing Process Automation Tooling in a Local Container using Podman

Recently the open source community project called Podman announced that there was solid support for using its container tooling to replace docker on your local development machine. Ring in the joyous music and off we go to explore how we can get back to basics without the issues of licensing around the developer desktop container tooling.

Note, the rest of this tutorial will be based on the current version of Podman at the time of publication, v3.3.1.

What Is Data Engineering? Skills and Tools Required

In the last decade, as most organizations began receiving advanced change, data scientists and data engineers have developed into two separate jobs, obviously, with specific covers. The business generates data constantly from people and products. Every event is a snapshot of company functions (and dysfunctions) such as revenue, losses, third-party partnerships, and goods received. But if the data isn't explored, there will be no insights gained. The intention of data engineering is to help the process and make it workable for buyers of data. In this article, we’ll explore the definition of data engineering, data engineering skills, what data engineers do and their responsibilities, and the future of data engineering.

Data Engineering: What Is It?

In the world of data, a data scientist is just comparable to the information or data they approach. Most companies store their information or data in an assortment of arrangements across data sets and text formats. This is the situation where data engineering enters. In simple form, data engineering means organizing and designing the data, which is done by the data engineers. They construct data pipelines that change that information, organize them, and make them useful. Data engineering is similarly as significant as data science.  However, data engineering requires realizing how to get an incentive form of data, just as the commonsense designing abilities to move data from guide A toward point B without defilement.

UI Interactions & Animations Roundup #19

These past couple of weeks we’ve been collecting some motion gems for you that will hopefully spark some true inspiration! There’s some creative experimentation and novel moves are being explored, so don’t miss out on this roundup that has lots of animation variety. It’s interesting how specific combinations of colors and typography go very well with certain animation speeds and easings, something that, when done right, can bring a web experience to the next level.

I really hope you enjoy this set!

Home Exploration – Quartet Cie

by Thierry Rolin

TAE – Web Design for Travel & Tourism

by Outcrowd

Satellite Landing Page Animation

by Conceptzilla

Shoplon E-commerce UI Kit

by mr.alidoost

Wonderland Photo Collection

by Vitalii Burhonskyi

Arcade Club

by Ruslan Siiz

Nature

by Roman Salo

Essentialism

by Ali Zafar Iqbal

NASA. Main

by Evgeny UPROCK

Insomnia Website Interactions

by tubik

Betabank – Credit Card Product Page

by George Ilushenko

71ST

by Jurica Koletic

Spot Sneaker Store

by Ruslan Siiz

Visvim – Kinetic Type

by Domagoj Štrok

Brand Identity For Hops & Skips beer

by Afterglow

GetFit Landing Page

by Dede Dwiyansyah

Mara Camp Safari Travel website

by Outline2Design

DEKO — Hotel Website Animation

by Alex Tkachev

Fashion photographer website

by Den Potapov

Vinyl – Web Design for Vinyl Store

by Outcrowd

The post UI Interactions & Animations Roundup #19 appeared first on Codrops.

Get Ready for Jakarta EE 10!

Jakarta EE 10 is on its way! This is the third release of Jakarta EE since the transition from the Java Community Process (JCP) to Eclipse Foundation. In this session, we will go through every aspect of Jakarta EE 10 and how this release lowers the barriers of entry, eases the migration, and lays the foundation for a platform for future innovation. We will also look ahead towards what future releases may bring. There will be a demo including converting from the javax.* to jakarta.* namespace as well as looking at available implementations.

Simple code: Simplicity

Simplest solutions are usually the best solutions.

We as software developers work with hard problems and solve a lot of small problems every day. Solving a hard problem itself is a hard job. Though in my opinion it's not enough to solve a hard problem in any possible way but a hard problem should be solved with a simple solution. When a developer comes up with a simple solution to a hard problem then they can declare the problem solved.

First a disclaimer. Coming up with a simple solution to a hard problems is itself a very hard problem and takes a lot of time, effort and practice.

I've seen my share of "clever" solutions for hard problems and the problem with those is that usually the solution itself is so hard to understand that depending on the size of the problem it may take a developer from hours to days or even weeks to understand how that "clever" solution works. It's a rare occasion when a developer has come up with a simple solution to a hard problem. So simple that it needs to be read only once and it makes sense. I dare to say I've made a few of these solutions and I've also seen these from other developers but way less often than hard to understand solutions for hard problems.

So how does one come up with simple solutions to hard problems?
First step is to split the problem to smaller problems. How this works for me is that I try to identify smaller problem areas within the original problem. Once I have identified smaller problems I can usually find yet smaller problems to solve within those and I continue to do it until I have a plan, a set of tasks or small problems to solve. Usually I identify even more problems to solve once I start to work on them and I just add them to the list of small problems to solve. With this iterative process I can solve the problem one small piece at a time.

Splitting the problem to multiple smaller problems gives other benefits too. Each small problem can be tested individually and the big problem's tests work as acceptance tests for the whole set of small problems. Smaller problems are easier to solve and therefore the code is easier to write and when the code is easier to write it's easier to write readable code.

In a ideal situation the big problem would be solved by sequentially calling functions that solve a smaller problem within the problem space. In a way it could be thought like the solution to a problem can be solved by a function introduced in a interface. That interface is tested with a acceptance tests and it can be tested with mocks or spies to verify it calls the correct functions in correct order with correct parameters. The implementation of the interface is actually a series of function calls that each solve a portion of the problem. Each of those functions is tested with unit tests. Each of functions can be written as easily readable by keeping them small and naming the functions and things within those functions meaningfully. When a function is small and solves a single problem the solution is easy to define with immutable data structures or by avoiding mutating the variables. Also when each function works as it's own unit it's easier to isolate integration tests to those functions.

This is how simple solutions are crafted and this is how all the topics I have covered earlier are tied together to create simple, readable, verified and long lasting solutions to problems.

With all this tied up what started with a working title "My version of clean code" could also be simplified and after thinking about it more while writing these posts I decided to name the approach and conventions as "Simple code".