Skip to content

Commit

Permalink
feat: validate headings in markdown (#1060)
Browse files Browse the repository at this point in the history
  • Loading branch information
fpasquet committed Feb 1, 2024
1 parent 839af01 commit 02b9b7d
Show file tree
Hide file tree
Showing 227 changed files with 4,647 additions and 5,228 deletions.
21 changes: 4 additions & 17 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -1,22 +1,9 @@
root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 4
indent_style = space
insert_final_newline = true
max_line_length = 120
trim_trailing_whitespace = true

[*.js]
indent_size = 2

[*.scss]
indent_size = 2

[*.rb]
indent_size = 2

[Rakefile]
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
uses: actions/setup-node@v3
with:
bundler-cache: true
node-version: 16
node-version: 20

- name: Get yarn cache directory path
id: yarn-cache-dir-path
Expand Down
15 changes: 15 additions & 0 deletions Dockerfile-dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM node:20-bookworm

RUN npx -y playwright install --with-deps chromium

ENV NODE_ENV development

# set working directory
RUN mkdir -p /var/www/app \
&& chown -R node:node /var/www/app

WORKDIR /var/www/app

USER node:node

EXPOSE 3000
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,15 @@ Urls:
docker-compose up -d
```

Urls:
- Storybook: http://localhost:6006
- Website: http://localhost:5173
Url: http://localhost:5173


**4 - Start Storybook** :
```bash
docker-compose exec app yarn start:storybook
```

Url: http://localhost:6006

----------

Expand Down Expand Up @@ -128,6 +134,9 @@ keywords:
- keyword (limited to 10, must not be identical to the categories, used for SEO and search)
authors:
- author's username
seo:
title: title
description: description
---
Content of your article in markdown
```
Expand Down Expand Up @@ -215,6 +224,9 @@ authors:
- author's username
steps:
- slug of your steps (No space dashes instead)
seo:
title: title
description: description
---
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ authors:
- captainjojo
keywords: []
---
I am currently lead developer for a French press website with very high traffic. Along my previous work experiences, I was able to perform development on several other high volumetry sites as well.
I am currently lead developer for a French press website with very high traffic. Along my previous work experiences, I was able to perform development on several other high volumetry sites as well.

When with only a dozen or so servers you need to contain traffic peaks between 100,000 and 300,000 short term visitors, the cache ceases to be optional: it becomes an absolute necessity !
Your application may have the best performance possible, you will always be limited by your physical machines - even if this is no longer true in the cloud (with an unlimited budget) – Consequently you must make friends with the cache.
Expand All @@ -26,31 +26,31 @@ You must have often heard, or said yourself, "empty your cache", when testing th
</tr>
<tr>
<td><a class="mw-redirect" title="Firefox" href="https://fr.wikipedia.org/wiki/Firefox">Firefox</a></td>
<td><kbd>Ctrl</kbd> + <kbd>F5</kbd></td>
<td><kbd>Ctrl</kbd> + <kbd>F5</kbd></td>
</tr>
<tr>
<td><a title="Chrome" href="https://fr.wikipedia.org/wiki/Chrome">Chrome</a></td>
<td><kbd>Ctrl</kbd> + <kbd>F5</kbd> ou Shift + <kbd>F5</kbd> ou <kbd>Ctrl</kbd> + Shift + <kbd>N </kbd></td>
<td><kbd>Ctrl</kbd> + <kbd>F5</kbd> ou Shift + <kbd>F5</kbd> ou <kbd>Ctrl</kbd> + Shift + <kbd>N </kbd></td>
</tr>
<tr>
<td><a class="mw-redirect" title="Safari (logiciel)" href="https://fr.wikipedia.org/wiki/Safari_(logiciel)">Safari</a></td>
<td><kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>E</kbd></td>
<td><kbd>Ctrl</kbd> + <kbd>Alt</kbd> + <kbd>E</kbd></td>
</tr>
<tr>
<td><a title="Internet Explorer" href="https://fr.wikipedia.org/wiki/Internet_Explorer">Internet Explorer</a></td>
<td><kbd>Ctrl</kbd> + <kbd>F5</kbd></td>
<td><kbd>Ctrl</kbd> + <kbd>F5</kbd></td>
</tr>
<tr>
<td><a title="Opera" href="https://fr.wikipedia.org/wiki/Opera">Opera</a></td>
<td><kbd>Ctrl</kbd> + <kbd>F12</kbd></td>
<td><kbd>Ctrl</kbd> + <kbd>F12</kbd></td>
</tr>
</tbody>
</table>
source: https://fr.wikipedia.org/wiki/Cache_web

Don't worry, there is a solution; the purpose of this article is to enable you to finally get to grips with the HTTP cache.

### Principle of the HTTP Cache
## Principle of the HTTP Cache

The HTTP cache uses the same principle as all caches; it is simply a key/value recording.

Expand Down Expand Up @@ -121,7 +121,7 @@ This also allows you to manage response 304 which allows the server to send only
There is another header with the same principle; it is the Etag header, which is configured with a 'string' generated by the server which changes according to the content of the page.

```sh
Etag: home560
Etag: home560
```

Be careful, the calculation of the ETag must be very carefully considered because it governs the cache time of the page, so it must be calculated with the dynamic data of the page.
Expand All @@ -134,36 +134,36 @@ Vary: Cookie User-agent
As its name indicates, it allows you to vary the cache by using another header, e.g.: 'User-agent' which allows you to store, for one URL, all the pages for each user-agent (e.g.: mobile page and desktop page). The cookie allows you to store one page per cookie (hence per user).
We have just made a rather comprehensive tour of the possible configurations for the HTTP cache, but it is also possible to add one's own headers. Before moving forward on this topic, we shall look at the architecture of the HTTP cache.

### Classic HTTP cache architecture
## Classic HTTP cache architecture
You know how to configure your cache like a professional.

> But where should you place your cache?
The HTTP cache can be used in several places in your architecture; each place has specificities and allows an improvement in performance.

#### The browser
### The browser

This is the HTTP cache closest to your user; this allows it to be very fast. The only concern is that it is linked to the user; consequently it may be empty quite often, for example on the first connection. It may also be emptied by the user or even disabled. Consequently the resilience of this cache is not allocated to you; it must therefore be paired with another HTTP cache.

#### The CDN
### The CDN

The Content Delivery Network is a network of computers to serve content (https://fr.wikipedia.org/wiki/Content_delivery_network). It is external to the architecture and its specialty is geolocation, it therefore allows a user to go onto the nearest CDN. You have probably already used one when you use a JS tag, e.g.: jquery, angular, etc. …, and if you use the URL provided rather than the downloaded file.
The main advantage of the CDN is to have many servers across the world and allow you not to receive all the traffic to the site on your servers. If you have already worked for a high traffic site, the CDN is the best way not to maintain 10,000 servers for your application. The cost of a CDN is often linked to the bandwidth, hence it is important to cache only what you need and use the most possible 304s.

#### The cache proxy, Varnish
### The cache proxy, Varnish

It acts like the CDN, and the CDN belongs to the architecture. It often concerns servers that you maintain, it requires a lot of RAM (the storage is done there). The Varnish or other cache technology allows finer configurations than a CDN and above all allows the use of one's own headers. It also allows use of the ESI that we shall see in the next chapter. (Akamai, the inventor of ESI is a CDN)

#### The Web Server
### The Web Server

The web server also allows you to use the HTTP cache, it is generally used for the cache of assets (JS, CSS, images, etc.). Like Varnish, its advantage is to be very finely configurable.
![Architecture Http]({BASE_URL}/imgs/articles/2016-06-29-le-cache-http-votre-meilleur-ami/untitled.png)

### Customizing your HTTP cache
## Customizing your HTTP cache

The advantage of the HTTP cache is that it is very simple to use, and that most of the web frameworks put in place simple interfaces to use it. Despite a great number of features, we always need more; it is for this reason that for a project on a high traffic site we place two Varnish custom headers, which can help.

#### The catalog
### The catalog


```sh
Expand Down Expand Up @@ -196,7 +196,7 @@ if (req.method == "BAN") {
}
```

#### The Grace
### The Grace

```sh
X-Varnish-Grace: 300
Expand Down Expand Up @@ -245,7 +245,7 @@ sub vcl_backend_response {
}
```

### The ESIs
## The ESIs

You are now an expert in the use of the HTTP cache, only one thing is left to be understood: ESIs.
Edge Side Include (ESI) allows you to use the full power of Varnish. As stated above, this technology was invented by [Akamai](https://www.akamai.com/fr/), one of the most famous CDNs.
Expand Down
54 changes: 27 additions & 27 deletions _articles/en/2016-07-19-behat-structure-functional-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,26 +19,26 @@ In order to ensure that your application is running well, it's important to writ

Behat is the most used tool with Symfony to handle your functional tests and that's great because it's really a complete suite.

You should nevertheless know how to use it wisely in order to cover useful and complete test cases and that's the goal of this blog post.
You should nevertheless know how to use it wisely in order to cover useful and complete test cases and that's the goal of this blog post.

# Introduction
## Introduction

## Functional testing: what's that?
### Functional testing: what's that?
When we are talking about functional testing we often mean that we want to automatize human-testing scenarios over the application.

However, it is important to write the following test types to cover the functional scope:
* `Interface tests`: Goal here is to realize interface verifications to ensure that our web application features can be used over a browser,
* `Integration tests`: Goal of these tests is to ensure that sour code (already unit-tested) which makes the application running is acting as it should when all components are linked together.

Idea is to develop and run both integration tests and interface tests with Behat.
Before we can go, please note that we will use a `Selenium` server which will receive orders by `Mink` (a Behat extension) and will pilot our browser (Chrome in our configuration).
Before we can go, please note that we will use a `Selenium` server which will receive orders by `Mink` (a Behat extension) and will pilot our browser (Chrome in our configuration).

To be clear on the architecture we will use, here is a scheme that will resume the role of all elements:

!["Behat architecture schema"]({BASE_URL}/imgs/articles/2016-07-19-behat-structure-functional-tests/behat_en.jpg)

## Behat set up
First step is to install Behat and its extensions as dependencies in our `composer.json` file:
### Behat set up
First step is to install Behat and its extensions as dependencies in our `composer.json` file:

```json
"require-dev": {
Expand All @@ -51,7 +51,7 @@ First step is to install Behat and its extensions as dependencies in our `compos
}
```

In order to make your future contexts autoloaded, you also have to add this little `PSR-4` section:
In order to make your future contexts autoloaded, you also have to add this little `PSR-4` section:

```json
"autoload-dev": {
Expand All @@ -61,7 +61,7 @@ In order to make your future contexts autoloaded, you also have to add this litt
}
```

Now, let's create our **behat.yml** file in our project root directory in order to define our tests execution.
Now, let's create our **behat.yml** file in our project root directory in order to define our tests execution.

Here is the configuration file we will start with:

Expand Down Expand Up @@ -89,20 +89,20 @@ default:
output_path: %paths.base%/web/reports/behat
```

We will talk of all of these sections in their defined order so let's start with the **suites** section which is empty at this time but we will implement it later when we will have some contexts to add into it.
We will talk of all of these sections in their defined order so let's start with the **suites** section which is empty at this time but we will implement it later when we will have some contexts to add into it.

Then, we load some Behat extensions:

* `Behat\Symfony2Extension` will allow us to inject Symfony services into our contexts (useful for integrations tests mostly),
* `Behat\MinkExtension` will allow us to pilot Selenium (drive itself the Chrome browser) so we fill in all the necessary information like the hostname, the Selenium server port number and the base URL we will use for testing,
* `emuse\BehatHTMLFormatter\BehatHTMLFormatterExtension` will generate a HTML report during tests execution (which is great to show to our customer for instance).
* `Behat\MinkExtension` will allow us to pilot Selenium (drive itself the Chrome browser) so we fill in all the necessary information like the hostname, the Selenium server port number and the base URL we will use for testing,
* `emuse\BehatHTMLFormatter\BehatHTMLFormatterExtension` will generate a HTML report during tests execution (which is great to show to our customer for instance).

Finally, in the `formatters` section we keep the `pretty` formatter in order to keep an output in our terminal and the HTML reports will be generated at the same time in the `web/reports/behat` directory in order to make them available over HTTP (it should not be a problem as you should not execute functional tests in production, be careful to restrict access in this case).
Now that Behat is ready and configured we will prepare our functional tests that we will split into two distinct Behat suites: `integration` and `interface`.
Finally, in the `formatters` section we keep the `pretty` formatter in order to keep an output in our terminal and the HTML reports will be generated at the same time in the `web/reports/behat` directory in order to make them available over HTTP (it should not be a problem as you should not execute functional tests in production, be careful to restrict access in this case).
Now that Behat is ready and configured we will prepare our functional tests that we will split into two distinct Behat suites: `integration` and `interface`.

# Writing functional tests (features)
## Writing functional tests (features)
In our example, we will write tests in order to ensure that a new user can register over a registration page.
We will have to start by writing our tests scenarios (in a `.feature` file) that we will put into a `features/` directory located at the project root directory.
We will have to start by writing our tests scenarios (in a `.feature` file) that we will put into a `features/` directory located at the project root directory.

So for instance, we will have the following scenario:

Expand All @@ -121,7 +121,7 @@ Scenario: I register when I fill my username and password only
Then I should see the registration confirmation</pre>
```

# Integration tests
## Integration tests

As said previously, these tests are here to ensure all code written for the registration page can be executed and linked without any errors.

Expand Down Expand Up @@ -207,7 +207,7 @@ class IntegrationRegisterContext implements Context

Integration test for this part is now done for our feature. Let's write the interface test now!

# Interface tests
## Interface tests

This test will be based on the same feature file without modifying the original written scenarios we wrote at the beginning.
That's why it is important to write a generic test that can be implemented both in an integration test and in an interface test.
Expand Down Expand Up @@ -268,9 +268,9 @@ class MinkRegisterContext extends MinkContext
We just implemented an interface test based on the same scenario that the one we used for integration test so this class has exactly the same four methods with the same Behat annotations that we have implemented in our integration test class.
The only difference here is that in this context we ask Mink to ask to Selenium to do actions on the interface of our application by executing a browser instead of testing the code itself.

# Context definition
## Context definition

One more thing now, we have to add previously created contexts in our `suites` section in the `behat.yml` configuration file.
One more thing now, we have to add previously created contexts in our `suites` section in the `behat.yml` configuration file.

```yaml
suites:
Expand All @@ -288,19 +288,19 @@ suites:
- Acme\Tests\Behat\Context\Registration\MinkRegisterContext: []
```

It is important to see here that we can clearly split these kind of tests into two distinct parts `integration` and `interface`: each one will be executed with its own contexts.
It is important to see here that we can clearly split these kind of tests into two distinct parts `integration` and `interface`: each one will be executed with its own contexts.

Also, as we have loaded the Symfony2 extension during the Behat set up, we have the possibility to inject Symfony services in our contexts and that case occurs here with the `acme.registration.registerer` service.
Also, as we have loaded the Symfony2 extension during the Behat set up, we have the possibility to inject Symfony services in our contexts and that case occurs here with the `acme.registration.registerer` service.

# Tests execution
## Tests execution

In order to run all tests, simply execute in the project root directory: `bin/behat -c behat.yml`.
If you want to run the integration tests only: `bin/behat -c behat.yml --suite=integration`.
HTML report will be generated under the `web/reports/behat/` as specified in the configuration that will allow you to have a quick overview of failed tests which is cool when you have a lot of tests.
HTML report will be generated under the `web/reports/behat/` as specified in the configuration that will allow you to have a quick overview of failed tests which is cool when you have a lot of tests.

# Link multiple contexts together
## Link multiple contexts together

At last, sometime you could need information from another context. For instance, imagine that you have a second step just after the register step. You will have to create two new `IntegrationProfileContext` and `MinkProfileContext` contexts.
At last, sometime you could need information from another context. For instance, imagine that you have a second step just after the register step. You will have to create two new `IntegrationProfileContext` and `MinkProfileContext` contexts.

We will only talk about integration context in the following to simplify understanding.

Expand Down Expand Up @@ -340,9 +340,9 @@ class IntegrationProfileContext implements Context
}
```

You now have an accessible property **$registerContext** and can access informations from this context.
You now have an accessible property **$registerContext** and can access informations from this context.

# Conclusion
## Conclusion

Everything starts from well-written tests which have to be thoughtful in order to allow a technical implementation on both integration tests and interface tests.

Expand Down
Loading

0 comments on commit 02b9b7d

Please sign in to comment.