Cypress Tests: Preserve Cookies and Keep Login Session Active

By default, Cypress resets the sessions before each test. If you are logged in scenario first test (ex: it() block), then in the second test (second it block), and you are performing some other task, you are automatically taken back to the login page. This happens because Cypress doesn’t keep the session; it creates a new session. In end-to-end testing, mostly, users will be logged first and then all the workflow will be checked. This article explains the simplest way to overcome this problem using just a few lines of code.

Let me explain this scenario:

A Primer on the Different Types of Browser Storage

In back-end development, storage is a common part of the job. Application data is stored in databases, files in object storage, transient data in caches… there are seemingly endless possibilities for storing any sort of data. But data storage isn’t limited only to the back end. The front end (the browser) is equipped with many options to store data as well. We can boost our application performance, save user preferences, keep the application state across multiple sessions, or even different computers, by utilizing this storage.

In this article, we will go through the different possibilities to store data in the browser. We will cover three use cases for each method to grasp the pros and cons. In the end, you will be able to decide what storage is the best fit for your use case. So let’s start!

The localStorage API

localStorage is one of the most popular storage options in the browser and the go-to for many developers. The data is stored across sessions, never shared with the server, and is available for all pages under the same protocol and domain. Storage is limited to ~5MB.

Surprisingly, the Google Chrome team doesn’t recommend using this option as it blocks the main thread and is not accessible to web workers and service workers. They launched an experiment, KV Storage, as a better version, but it was just a trial that doesn’t seem to have gone anywhere just yet.

The localStorage API is available as window.localStorage and can save only UTF-16 strings. We must make sure to convert data to strings before saving it into localStorage. The main three functions are:

  • setItem('key', 'value')
  • getItem('key')
  • removeItem('key')

They’re all synchronous, which makes it simple to work with, but they block the main thread.

It’s worth mentioning that localStorage has a twin called sessionStorage. The only difference is that data stored in sessionStorage will last only for the current session, but the API is the same.

Let’s see it in action. The first example demonstrates how to use localStorage for storing the user’s preferences. In our case, it’s a boolean property that turns on or off the dark theme of our site.

You can check the checkbox and refresh the page to see that the state is saved across sessions. Take a look at the save and load functions to see how I convert the value to string and how I parse it. It’s important to remember that we can store only strings.

This second example loads Pokémon names from the PokéAPI.

We send a GET request using fetch and list all the names in a ul element. Upon getting the response, we cache it in the localStorage so our next visit can be much faster or even work offline. We have to use JSON.stringify to convert the data to string and JSON.parse to read it from the cache.

In this last example, I demonstrate a use case where the user can browse through different Pokémon pages, and the current page is saved for the next visits.

The issue with localStorage, in this case, is that the state is saved locally. This behavior doesn’t allow us to share the desired page with our friends. Later, we will see how to overcome this issue.

We will use these three examples in the next storage options as well. I forked the Pens and just changed the relevant functions. The overall skeleton is the same for all methods.

The IndexedDB API

IndexedDB is a modern storage solution in the browser. It can store a significant amount of structured data — even files, and blobs. Like every database, IndexedDB indexes the data for running queries efficiently. It’s more complex to use IndexedDB. We have to create a database, tables, and use transactions.

Compared to localStorage , IndexedDB requires a lot more code. In the examples, I use the native API with a Promise wrapper, but I highly recommend using third-party libraries to help you out. My recommendation is localForage because it uses the same localStorage API but implements it in a progressive enhancement manner, meaning if your browser supports IndexedDB, it will use it; and if not, it will fall back to localStorage.

Let’s code, and head over to our user preferences example!

idb is the Promise wrapper that we use instead of working with a low-level events-based API. They’re almost identical, so don’t worry. The first thing to notice is that every access to the database is async, meaning we don’t block the main thread. Compared to localStorage, this is a major advantage.

We need to open a connection to our database so it will be available throughout the app for reading and writing. We give our database a name, my-db, a schema version, 1, and an update function to apply changes between versions. This is very similar to database migrations. Our database schema is simple: only one object store, preferences. An object store is the equivalent of an SQL table. To write or read from the database, we must use transactions. This is the tedious part of using IndexedDB. Have a look at the new save and load functions in the demo.

No doubt that IndexedDB has much more overhead and the learning curve is steeper compared to localStorage. For the key value cases, it might make more sense to use localStorage or a third-party library that will help us be more productive.

Application data, such as in our Pokémon example, is the forte of IndexedDB. You can store hundreds of megabytes and even more in this database. You can store all the Pokémon in IndexedDB and have them available offline and even indexed! This is definitely the one to choose for storing app data.

I skipped the implementation of the third example, as IndexedDB doesn’t introduce any difference in this case compared to localStorage. Even with IndexedDB, the user will still not share the selected page with others or bookmark it for future use. They’re both not the right fit for this use case.

Cookies

Using cookies is a unique storage option. It’s the only storage that is also shared with the server. Cookies are sent as part of every request. It can be when the user browses through pages in our app or when the user sends Ajax requests. This allows us to create a shared state between the client and the server, and also share state between multiple applications in different subdomains. This is not possible by other storage options that are described in this article. One caveat: cookies are sent with every request, which means that we have to keep our cookies small to maintain a decent request size.

The most common use for cookies is authentication, which is out of the scope of this article. Just like the localStorage, cookies can store only strings. The cookies are concatenated into one semicolon-separated string, and they are sent in the cookie header of the request. You can set many attributes for every cookie, such as expiration, allowed domains, allowed pages, and many more.

In the examples, I show how to manipulate the cookies through the client-side, but it’s also possible to change them in your server-side application.

Saving the user’s preferences in a cookie can be a good fit if the server can utilize it somehow. For example, in the theme use case, the server can deliver the relevant CSS file and reduce potential bundle size (in case we’re doing server-side-rendering). Another use case might be to share these preferences across multiple subdomain apps without a database.

Reading and writing cookies with JavaScript is not as straightforward as you might think. To save a new cookie, you need to set document.cookie — check out the save function in the example above. I set the dark_theme cookie and add it a max-age attribute to make sure it will not expire when the tab is closed. Also, I add the SameSite and Secure attributes. These are necessary because CodePen uses iframe to run the examples, but you will not need them in most cases. Reading a cookie requires parsing the cookie string.

A cookie string looks like this:

key1=value1;key2=value2;key3=value3

So, first, we have to split the string by semicolon. Now, we have an array of cookies in the form of key1=value1, so we need to find the right element in the array. In the end, we split by the equal sign and get the last element in the new array. A bit tedious, but once you implement the getCookie function (or copy it from my example :P) you can forget it.

Saving application data in a cookie can be a bad idea! It will drastically increase the request size and will reduce application performance. Also, the server cannot benefit from this information as it’s a stale version of the information it already has in its database. If you use cookies, make sure to keep them small.

The pagination example is also not a good fit for cookies, just like localStorage and IndexedDB. The current page is a temporary state that we would like to share with others, and any of these methods do not achieve it.

URL storage

URL is not a storage, per se, but it’s a great way to create a shareable state. In practice, it means adding query parameters to the current URL that can be used to recreate the current state. The best example would be search queries and filters. If we search the term flexbox on CSS-Tricks, the URL will be updated to https://css-tricks.com/?s=flexbox. See how easy it is to share a search query once we use the URL? Another advantage is that you can simply hit the refresh button to get newer results of your query or even bookmark it.

We can save only strings in the URL, and its maximum length is limited, so we don’t have so much space. We will have to keep our state small. No one likes long and intimidating URLs.

Again, CodePen uses iframe to run the examples, so you cannot see the URL actually changing. Worry not, because all the bits and pieces are there so you can use it wherever you want.

We can access the query string through window.location.search and, lucky us, it can be parsed using the URLSearchParams class. No need to apply any complex string parsing anymore. When we want to read the current value, we can use the get function. When we want to write, we can use set. It’s not enough to only set the value; we also need to update the URL. This can be done using history.pushState or history.replaceState, depending on the behavior we want to accomplish.

I wouldn’t recommend saving a user’s preferences in the URL as we will have to add this state to every URL the user visits, and we cannot guarantee it; for example, if the user clicks on a link from Google Search.

Just like cookies, we cannot save application data in the URL as we have minimal space. And even if we did manage to store it, the URL will be long and not inviting to click. Might look like a phishing attack of sorts.

Just like our pagination example, the temporary application state is the best fit for the URL query string. Again, you cannot see the URL changes, but the URL updates with the ?page=x query parameter every time you click on a page. When the web page loads, it looks for this query parameter and fetches the right page accordingly. Now we can share this URL with our friends so they can enjoy our favorite Pokémon.

Cache API

Cache API is a storage for the network level. It is used to cache network requests and their responses. The Cache API fits perfectly with service workers. A service worker can intercept every network request, and using the Cache API, it can easily cache both the requests. The service worker can also return an existing cache item as a network response instead of fetching it from the server. By doing so, you can reduce network load times and make your application work even when offline. Originally, it was created for service workers but in modern browsers the Cache API is available also in window, iframe, and worker contexts as-well. It’s a very powerful API that can improve drastically the application user experience.

Just like IndexedDB the Cache API storage is not limited and you can store hundreds of megabytes and even more if you need to. The API is asynchronous so it will not block your main thread. And it’s accessible through the global property caches.

To read more about the Cache API, the Google Chrome team has made a great tutorial.

Chris created an awesome Pen with a practical example of combining service workers and the Cache API.

Bonus: Browser extension

If you build a browser extension, you have another option to store your data. I discovered it while working on my extension, daily.dev. It’s available via chrome.storage or browser.storage, if you use Mozilla’s polyfill. Make sure to request a storage permission in your manifest to get access.

There are two types of storage options, local and sync. The local storage is self-explanatory; it means it isn’t shared and kept locally. The sync storage is synced as part of the Google account and anywhere you install the extension with the same account this storage will be synced. Pretty cool feature if you ask me. Both have the same API so it’s super easy to switch back-and-forth, if needed. It’s async storage so it doesn’t block the main thread like localStorage. Unfortunately, I cannot create a demo for this storage option as it requires a browser extension but it’s pretty simple to use, and almost like localStorage. For more information about the exact implementation, refer to Chrome docs.

Conclusion

The browser has many options we can utilize to store our data. Following the Chrome team’s advice, our go-to storage should be IndexedDB. It’s async storage with enough space to store anything we want. localStorage is not encouraged, but is easier to use than IndexedDB. Cookies are a great way to share the client state with the server but are mostly used for authentication.

If you want to create pages with a shareable state such as a search page, use the URL’s query string to store this information. Lastly, if you build an extension, make sure to read about chrome.storage.


The post A Primer on the Different Types of Browser Storage appeared first on CSS-Tricks.

You can support CSS-Tricks by being an MVP Supporter.

Why every website wants you to accept its cookies

I'm probably in the minority on this, but I've never ever built one of those "This site uses cookies, here's some kind of explanation of why, and please click this OK button to accept that" bars that feels like they are on half of the internet.

Emily Stewart:

Most of us just tediously click “yes” and move on. If you reject the cookie tracking, sometimes, the website won’t work. But most of the time, you can just keep browsing. They’re not too different from the annoying pop-up ads we all ignore when we’re online.

I'm extra-ignorant in that don't even really get why they exist, despite being a professional web site builder.

Emily does a good job of rounding up the answer. It's probably about what you think it is: a better safe than sorry play. Better annoy some users than get sued out of existence.

It's also interesting that it's not just one particular regulation that has people doing this. GDPR is a big one (despite being fairly light on mentions of cookies at all), but it's really a couple of different regulations, including likely-upcoming ones, that have people implementing these obnoxious pop-ups.

I'm probably the weirdo that would rather get sued than show a fricking cookie banner.

Speaking of cookies though, and things that I'm ignorant about, I asked this question not long ago:

My brain didn't have an answer at the time. If I was pressed on it, I'd probably answer that it's just snake oil, and that those checkboxes don't actually do anything.

From the thread, the answer seems to be that most sites use cookies to store your logged-in user session. Cookies have expiration dates. The "Remember me?" option makes the cookie have a longer expiration date than if you didn't check it.

The whole thread there is pretty fun. Lots of useful things and lots more jokes. I'm on board with the idea that anytime you check that box, some server, somewhere, plays this.

Update

There is some fair pushback on my take above.

I'm probably the weirdo that would rather get sued than show a fricking cookie banner.

Huge thanks to Laura Kalbag for having a conversation with me about this. Here's the situation:

As I write, this website is illegal in Europe. And maybe everywhere? I'm not quite clear on that yet.

It's because this site sets some cookies that are "non-essential" (unlike, say, a login cookie, which is "essential"). For example, I've written code myself (at the request of advertisers) to include an <img src="pixel.gif"> with their display advertisement. The purpose of that image is to track impressions, but it also can and does set a cookie, and probably can and does to other things besides track impressions, like attempt to show "targetted" ads. As I write, both MailChimp and Wufoo use these "tracking pixels" in ads that are running on this site. (I've actually reached out to see if removing them would be a deal-breaker or not, let's see!)

So, because of those non-essential cookies, I'm required to show UI that asks for user's consent. And here's an important aspect as well: before they've answered, or if they don't consent, I shouldn't be including those tracking pixels at all (because I can't control whether or not they set a cookie). That's some fancy coding stuff that I just haven't done. To do it without JavaScript is even fancier dancing.

So theoretically, legal action could be taken against me for this. There is some irony to the fact that a lot of sites with cookie-consent UI don't even implement the opt-in nature of them, making them useless. And some extra irony in that to really do this correctly, it probably also requires cookies, which you'd be required to tell them about. It's not the existence of the UI that counts, it's actually not setting the cookies unless you have consent. I'm not particularly worried about being sued. Apparently so far it's not governments taking actions but individual lawyers taking out cases on behalf of people. I would guess these will gain steam in coming years.

I am more compelled by the right thing to do argument, in that if you're going to put a cookie on someone's computer, you should tell them what it's for and ask them if it's OK first.

So I'm in a predicament. I don't want to build a cookie consent UI. It will be difficult to program, technical debt to maintain, and worse, be annoying to users. I'd rather see if we can just ditch anything setting a third-party cookie, so I'm going down that road first.

Direct Link to ArticlePermalink

The post Why every website wants you to accept its cookies appeared first on CSS-Tricks.

Blocking Third-Party Hands from the Cookie Jar

Third-party cookies are set on your computer from domains other than the one that you're actually on right now. For example, if I log into css-tricks.com, I'll get a cookie from css-tricks.com that handles my authentication. But css-tricks.com might also load an image from some other site. A common tactic in online advertising is to render a "tracking pixel" image (well named, right?) that is used to track advertising impressions. That request to another site for the image (say, ad.doubleclick.com) also can set a cookie.

Eric Lawrence explains the issue:

The tracking pixel’s cookie is called a third party cookie because it was set by a domain unrelated to the page itself.

If you later visit B.textslashplain.com, which also contains a tracking pixel from ad.doubleclick.net, the tracking pixel’s cookie set on your visit to A.example.com is sent to ad.doubleclick.net, and now that tracker knows that you’ve visited both sites. As you browse more and more sites that contain a tracking pixel from the same provider, that provider can build up a very complete profile of the sites you like to visit, and use that information to target ads to you, sell the data to a data aggregation company, etc.

But times are a changin'. Eric goes on to explain the browser landscape:

The default stuff is the big deal, because all browsers offer some way to block third-party cookies. But of course, nobody actually does it. Jeremy:

It’s hard to believe that we ever allowed third-party cookies and scripts in the first place. Between them, they’re responsible for the worst ills of the World Wide Web.

2019 is the year we apparently reached the breaking point.

The post Blocking Third-Party Hands from the Cookie Jar appeared first on CSS-Tricks.

Weekly Platform News: Tracking via Web Storage, First Input Delay, Navigating by Headings

In this week's roundup, Safari takes on cross-site tracking, the delay between load and user interaction is greater on mobile, and a new survey says headings are a popular way for screen readers to navigate a webpage.

Let's get into the news.

Safari’s tracking prevention limits web storage

Some social networks and other websites that engage in cross-site tracking add a query string or fragment to external links for tracking purposes. (Apple calls this "abuse of link decoration.")

When people navigate from websites with tracking abilities to other websites via such "decorated" links, Safari will expire the cookies that are created on the loaded web pages after 24 hours. This has led some trackers to start using other types of storage (e.g. localStorage) to track people on websites.

As a new countermeasure, Safari will now delete all non-cookie website data in these scenarios if the user hasn’t interacted with the website for seven days.

The reason why we cap the lifetime of script-writable storage is simple. Site owners have been convinced to deploy third-party scripts on their websites for years. Now those scripts are being repurposed to circumvent browsers’ protections against third-party tracking. By limiting the ability to use any script-writable storage for cross-site tracking purposes, [Safari’s tracking prevention] makes sure that third-party scripts cannot leverage the storage powers they have gained over all these websites.

(via John Wilander)

First Input Delay is much worse on mobile

First Input Delay (FID), the delay until the page is able to respond to the user, is much worse on mobile: Only 13% of websites have a fast FID on mobile, compared to 70% on desktop.

Tip: If your website is popular enough to be included in the Chrome UX Report, you can check your site’s mobile vs. desktop FID data on PageSpeed Insights.

(via Rick Viscomi)

Screen reader users navigate web pages by headings

According to WebAIM’s recent screen reader user survey, the most popular screen readers are NVDA (41%) and JAWS (40%) on desktop (primary screen reader) and VoiceOver (71%) and TalkBack (33%) on mobile (commonly used screen readers).

When trying to find information on a web page, most screen reader users navigate the page through the headings (<h1>, <h2>, <h3>, etc.).

The usefulness of proper heading structures is very high, with 86.1% of respondents finding heading levels very or somewhat useful.

Tip: You can check a web page’s heading structure with W3C’s Nu Html Checker (enable the "outline" option).

(via WebAIM)

More news...

Read even more news in my weekly Sunday issue that can be delivered to you via email every Monday morning.

More News →

The post Weekly Platform News: Tracking via Web Storage, First Input Delay, Navigating by Headings appeared first on CSS-Tricks.

How to Use Cookies in Spring Boot

An HTTP Cookie (also known as a web cookie or browser cookie) is a small piece of information stored by the server in the user's browser. The server sets the cookies while returning the response for a request made by the browser. The browser stores the cookies and sends them back with the next request to the same server. Cookies are generally used for session management, user-tracking, and to store user preferences.

Cookies help server remember the client across multiple requests. Without cookies, the server would treat every request as a new client.

Weekly Platform News: Event Timing, Google Earth for Web, undead session cookies

Šime posts regular content for web developers on webplatform.news.

In this week's news, Wikipedia helps identify three slow click handlers, Google Earth comes to the web, SVG properties in CSS get more support, and what to do in the event of zombie cookies.

Tracking down slow event handlers with Event Timing

Event Timing is experimentally available in Chrome (as an Origin Trial) and Wikipedia is taking part in the trial. This API can be used to accurately determine the duration of event handlers with the goal of surfacing slow events.

We quickly identified 3 very frequent slow click handlers experienced frequently by real users on Wikipedia. [...] Two of those issues are caused by expensive JavaScript calls causing style recalculation and layout.

(via Gilles Dubuc)

Google Earth for Web beta available

The preview version of Google Earth for Web (powered by WebAssembly) is now available. You can try it out in Chromium-based browsers and Firefox — it runs single-threaded in browsers that don’t yet have (re-)enabled SharedArrayBuffer — but not in Safari because of its lack of full support for WebGL2.

(via Jordon Mears)

SVG geometry properties in CSS

Firefox Nightly has implemented SVG geometry properties (x, y, r, etc.) in CSS. This feature is already supported in Chrome and Safari and is expected to ship in Firefox 69 in September.

See the Pen
Animating SVG geometry properties with CSS
by Šime Vidas (@simevidas)
on CodePen.

(via Jérémie Patonnier)

Browsers can keep session cookies alive

Chrome and Firefox allow users to restore the previous browser session on startup. With this option enabled, closing the browser will not delete the user’s session cookies, nor empty the sessionStorage of web pages.

Given this session resumption behavior, it’s more important than ever to ensure that your site behaves reasonably upon receipt of an outdated session cookie (e.g. redirect the user to the login page instead of showing an error).

(via Eric Lawrence)

The post Weekly Platform News: Event Timing, Google Earth for Web, undead session cookies appeared first on CSS-Tricks.

One Year After GDPR: The Lessons Digital Businesses Have Learned

GDPR in a year: changes that have already affected the tech world, ambiguities of GDPR interpretation, and how businesses are supposed to address them.

On May 25, 2018, the new European Data Protection Regulation became mandatory for execution in the European Union and the rest of the world where the data of EU citizens are being processed. Back in 2018, the majority of tech companies, hailing from the ad tech industry, in particular, were nervous about the inability to adapt their tech stack to the new standards in time. This was quite understandable, given that the consequences of a violation of the GDPR were promised to be severe, ranging from the following: a fine of 10 million euros, a 2 percent of the annual turnover, a fine of 20 million euros or 4 percent of annual turnover.

Weekly Platform News: Feature Policy, ECMAScript i18n API, Packaged PWAs

Šime posts regular content for web developers on webplatform.news.

New Feature Policy API in Chrome

Pete LePage: You can use the document.featurePolicy.allowedFeatures method in Chrome to get a list of all Feature Policy-controlled features that are allowed on the current page.

This API can be useful when implementing a feature policy (and updating an existing feature policy) on your website.

  1. Open your site in Chrome and run the API in the JavaScript console to check which Feature Policy-controlled features are allowed on your site.
  2. Read about individual features on featurepolicy.info and decide which features should be disabled ('none' value), and which features should be disabled only in cross-origin <iframe> elements ('self' value).
  3. Add the Feature-Policy header to your site’s HTTP responses (policies are separated by semicolons).
  4. Feature-Policy: geolocation 'self';sync-xhr 'none'
  5. Repeat Step 1 to confirm that your new feature policy is in effect. You can also scan your site on securityheaders.com.

In other news...

  • Dave Camp: Firefox now blocks cookies from known trackers by default (when the cookie is used in a third-party context). This change is currently in effect only for new Firefox users; existing users will be automatically updated to the new policy "in the coming months."
  • Pete LePage: Chrome for Android now allows websites to share images (and other file types) via the navigator.share method. See Web Platform News Issue 1014 for more information about the Web Share API. Ayooluwa Isaiah's post from yesterday is also a good reference on how to use it.
  • Valerie Young: The ECMAScript Internationalization APIs for date and time formatting (Intl.DateTimeFormat constructor), and number formatting (Intl.NumberFormat constructor) are widely supported in browsers.
  • Alan Jeffrey: Patrick Walton from Mozilla is working on a vector graphics renderer that can render text smoothly at all angles when viewed with an Augmented Reality (AR) headset. We plan to use it in our browsers for AR headsets (Firefox Reality).
  • Pinterest Engineering: Our progressive web app is now available as a standalone desktop application on Windows 10. It can be installed via the Microsoft Store, which "treats the packaged PWA as a first class citizen with access to Windows 10 feature APIs."
  • Jonathan Davis: The flow-root value for the CSS display property has landed in Safari Technology Preview. This value is already supported in Chrome and Firefox. See Web Platform News Issue 871 for a use case.

The post Weekly Platform News: Feature Policy, ECMAScript i18n API, Packaged PWAs appeared first on CSS-Tricks.

Supercookies

Supercookies, also known as evercookies or zombie cookies, are like browser cookies in that they can be used to track you, but are much harder to remove.

What Is a Supercookie?

The way I first heard supercookies described was as a cookie that you can appear to delete, but as soon as you do, software rewrites the cookie. Like the Hydra from Greek mythology, cutting off a head does no good because it grows back [1].

Cross-Site Cookie Manipulation

For years, we’ve been told to keep the values of sensitive session cookies unpredictable and complex in order to prevent attacks such as session enumeration. And, it made sense. If the session ID is complex, long, and cryptographically secure, it's almost impossible for an attacker to guess it.

However, from time to time, it's a good idea to look at recommended and widely-followed security practices and ask yourself: "Is this actually the most secure way to do things?" and "Is it enough?" You'd be surprised how often the answer is no. In this blog post, we discuss the security of PHP's session cookies in a shared hosting environment and explain why a cryptographically secure, random session ID is not enough to prevent attacks.