Skip to content

Commit

Permalink
Merge pull request #14 from jonyw4/feat/add-support-to-ptbr
Browse files Browse the repository at this point in the history
Feat/add support to ptbr
  • Loading branch information
jonyw4 authored Nov 18, 2021
2 parents f531581 + 76847b7 commit bb08a8b
Show file tree
Hide file tree
Showing 21 changed files with 199 additions and 40 deletions.
10 changes: 1 addition & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
# Blog

The code of my personal blog. **Is a working in progress.**

## Roadmap

1. [x] Configure the basics to create a article
2. [x] Create a first good article
3. [ ] Create the initial architecture
4. [ ] Create the minimum of the code to make it work
5. [ ] Deploy it
The code of my personal blog.

## Installation

Expand Down
2 changes: 1 addition & 1 deletion posts/.vale.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ StylesPath = .vale/StylesPath
MinAlertLevel = warning

# Only Markdown and .txt files; change to whatever you're using.
[*.{md,txt}]
[src/en/*.{md,txt}]
# List of styles to load.
BasedOnStyles = Openly
4 changes: 0 additions & 4 deletions posts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ This repository contains all the posts for the blog.

# Folder structure

`images`
The images of the posts.

`posts`
All posts are in this folder and are organized by language tag.

# Post files
Expand Down
2 changes: 1 addition & 1 deletion posts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"scripts": {
"spellcheck": "npm run spellcheck:en",
"spellcheck:en": "npm run spellcheck:en:fix -- --report",
"spellcheck:en:fix": "mdspell 'src/posts/en/**/*.md' --ignore-acronyms --en-us",
"spellcheck:en:fix": "mdspell 'src/en/**/*.md' --ignore-acronyms --en-us",
"lint:markdown": "markdownlint './src/**/*.md'",
"lint:markdown:fix": "npm run lint -- --fix",
"lint:spell": "vale --glob='*.{md}' src",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Another one is [The Testing Trophy](https://twitter.com/kentcdodds/status/960723

What I going to present here is based on Martin Fowler Pyramid of Testing with some little changes to represent **more general perspective of testing in front end**. Looks like this:

![Pyramid of Testing Front End Adapted](../../images/pyramid-of-testing-front-end.png "Pyramid of Testing Front End Adapted")
![Pyramid of Testing Front End Adapted](/images/pyramid-of-testing-front-end.png "Pyramid of Testing Front End Adapted")

These boundaries in the pyramid is what I call **levels** in this article. Each level represent a different purpose of testing. These levels can have different **types** of tests. Define tests types can be fuzzy and difficult because sometimes we don't have a good separation in the code.

Expand Down
88 changes: 88 additions & 0 deletions posts/src/pt-BR/testing-in-front-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
title: Teste no front end
description: Aprenda a fazer melhores testes em seu front end.
createdAt: '2021-11-03T02:11:11.781Z'
updatedAt: '2021-11-03T02:11:11.781Z'
tags:
- front-end
- testing
---

Ao longo da minha jornada como desenvolvedor, percebo que alguns colegas têm dificuldade em realizar testes no front end. Às vezes o problema é sobre não estar familiarizado, Ou sobre os detalhes de uma framework. Mas às vezes é **saber sobre o que você deve ser coberto**. Alguns livros (bons livros) até descrevem isso como difícil de fazer, dizendo que não é uma boa ideia. Mas estamos em 2021, e as ferramentas de teste evoluíram para fazer um bom teste de uma UI no front end.

Neste artigo, abordo os tipos comuns de teste, uma visão geral desses tipos e exemplos de ferramentas que você pode usar para fazer com que funcionem.

## Aviso

Ao ler este artigo, espero que você conhecesse os fundamentos dos testes e tenha alguma experiência nisso. **Se você não sabe nada sobre testes, este artigo não é para você**.

Dito isso, vamos lá!

## Pirâmide de teste

Você provavelmente conhece esta pirâmide específica de teste. Foi originalmente criada no [artigo de Martin Fowler sobre testes em Engenharia de Software](https://martinfowler.com/articles/practical-test-pyramid.html).

![A pirâmide de teste - Martin Fowler](https://martinfowler.com/articles/practical-test-pyramid/testPyramid.png)

Outro é [The Testing Trophy](https://twitter.com/kentcdodds/status/960723172591992832?lang=es), de Kent C Dodds, e dá outra perspectiva sobre o mesmo assunto.

![Troféu de teste - Kent C Dodds](https://pbs.twimg.com/media/DVUoM94VQAAzuws?format=jpg&name=900x900)

O que vou apresentar aqui é baseado na Pirâmide de Teste de Martin Fowler com algumas pequenas mudanças para representar **uma perspectiva mais geral de teste no front end**, e ela se parece com isso:

![Pirâmide de front-end de teste adaptada](/images/pyramid-of-testing-front-end.png "Pirâmide de front-end de teste adaptada")

Esses limites na pirâmide são o que chamo de **níveis** neste artigo. Cada nível representa um propósito diferente de teste. Esses níveis podem ter diferentes **tipos** de testes. Definir os tipos de teste pode ser confuso e difícil porque às vezes não temos uma boa separação no código.

Lembre-se de que o que estou apresentando aqui é baseado em **problemas comuns** que queremos resolver no desenvolvimento de UI (especialmente aplicativos da web). Mas **você não precisa ter tudo** em sua base de código, você sempre precisa identificar quais são eles de que você precisa antes de colocar em seu projeto.

Vamos começar com os **níveis e tipos mais comuns**.

### Nível de componente

Hoje em dia, é comum usar uma abordagem de componente. Começamos a criar componentes que têm pequenas partes de nosso layout e, em seguida, costuramos para criar uma página inteira. Usando isso como princípio, podemos ter alguns níveis de teste.
Neste nível, testamos o que vamos usar no código dos componentes. Idealmente, nossos componentes são desacoplados do resto do código. Portanto, podemos testá-lo independentemente, para trabalhar com qualquer entrada.

#### Snapshots test

Normalmente não usamos HTML bruto para criar nosso código. React, Vue e Angular vêm para resgatar. Portanto, o primeiro teste que você deseja ter em nossa base de código é o snapshot test. Com esse teste, podemos garantir que o componente feito no React, por exemplo, está renderizando a mesma saída em HTML que deveria estar no navegador.

A estrutura de teste dessas bibliotecas de componentes tem uma maneira de renderizar o HTML real e, então, você pode salvá-lo usando uma ferramenta de snapshot. [Jest tem uma documentação](https://jestjs.io/docs/snapshot-testing) para fazer isso.

#### Teste de interação

Os aplicativos da Web são feitos por interações e nossos componentes têm métodos que podem ser chamados pela interação de um usuário. Se um usuário clicar em um botão e abrir uma lista suspensa, você deseja testar se essas interações estão funcionando conforme o esperado e abrir a lista suspensa corretamente. Normalmente, os frameworks de teste têm uma maneira de testar isso também, um exemplo de ferramenta incrível é a [testing-library](https://testing-library.com/docs/).

#### Regressão visual

A UI é feita por pixels. Você quer ter certeza de que o visual dos componentes que você escreve não muda quando você faz uma refatoração. Com o teste de regressão visual você pode cuidar disso fazendo uma captura de tela do componente dentro de uma tela e compará-lo com a versão anterior. Às vezes pode ser complicado, como com animações, com sistemas operacionais diferentes, navegadores diferentes, resoluções de tela diferentes, etc. Mas geralmente é uma boa ideia tê-lo em sua base de código em algum nível.

Para fazer esse teste, você precisa colocar seu componente em um navegador real e fazer uma captura de tela dele. Portanto, você precisa de algo emulando o componente no navegador. Você pode fazer isso com [Cypress](https://www.cypress.io/), [Playwright](https://playwright.dev/), [Storybook](https://storybook.js.org/) com [Loki](https://loki.js.org/) ou [Chromatic](https://www.chromatic.com/).

### Nível de aplicação

#### Teste funcional

Teste funcional clássico, você pode usar para testar funções, auxiliares, camada de dados e etc.

#### Teste de integração

Você pode usar o termo teste de integração para descrever esses testes construindo o aplicativo inteiro, servir e tentar executar alguns dos principais cenários felizes

### End-to-End test

Você pode ver isso como um teste de recurso testando O aplicativo pela perspectiva do usuário. Esse teste é frágil e pode ser difícil de fazer, mas é uma boa maneira de testar o aplicativo. Você pode fazer isso com as mesmas ferramentas do nível do aplicativo.

## Conclusion

Espero que o conhecimento de alguns tipos e níveis diferentes de teste possa ajudá-lo a tomar boas decisões sobre quais testes você precisa em sua base de código. Embora, **o que e como é com você e sua equipe. Só não se esqueça de testar, seu futuro eu agradecerá**.

## References

Algumas referências e estudos adicionais sobre teste e arquitetura de front end. Faça um bom uso:

- [ARTICLE: Practical Test Pyramid by *Martin Fowler*](https://martinfowler.com/articles/practical-test-pyramid.html)
- [ARTICLE: Unit test by *Martin Fowler*](https://martinfowler.com/bliki/UnitTest.html)
- [ARTICLE: Gherkin Language by *Cucumber*](https://cucumber.io/docs/gherkin/)
- [ARTICLE: Atomic Design by *Brad Frost*](https://bradfrost.com/blog/post/atomic-web-design/)
- [BOOK: Clean Code by *Martin Robert C.*](https://www.amazon.com.br/dp/B001GSTOAM/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)
2 changes: 1 addition & 1 deletion webapp/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# `@blog/webapp`

This repository contains all the code of the webapp of the blog
This repository contains all the code of the webapp of the blog.
6 changes: 6 additions & 0 deletions webapp/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
i18n: {
locales: ["en-US", "pt-BR"],
defaultLocale: "en-US",
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { useLocale } from "../../global";

export function LanguageSelect() {
const { changeLocale, locale } = useLocale();
return (
<div>
<label htmlFor="language-select">🌐 Language</label>
<select
id="language-select"
onChange={(e) => changeLocale(e.target.value)}
value={locale}
>
<option value="en-US">English (US)</option>
<option value="pt-BR">Português (BR)</option>
</select>
</div>
);
}
6 changes: 5 additions & 1 deletion webapp/src/components/global/formatDate.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
const locale = "en-US";
let locale = "en-US";

export function setLocale(loc: string) {
locale = loc;
}

export function formatDate(date: Date) {
return date.toLocaleDateString(locale, {
Expand Down
2 changes: 2 additions & 0 deletions webapp/src/components/global/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './formatDate';
export * from './locale';
11 changes: 11 additions & 0 deletions webapp/src/components/global/locale.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from "react";

export interface Locale {
locale: string;
changeLocale: (locale: string) => void
}

// @ts-ignore
export const LocaleContext = React.createContext<Locale>();

export const useLocale = () => React.useContext(LocaleContext);
12 changes: 12 additions & 0 deletions webapp/src/components/global/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ h5 {
header {
text-align: center;
padding: 4rem 0 1rem 0;
display: flex;
align-items: center;
align-content: center;
justify-content: space-between;
}

header a {
Expand All @@ -108,4 +112,12 @@ article img {
display: block;
width: auto;
max-width: 400px
}

label, select {
font-size: 0.8rem;
}

label {
margin-right: 0.5em;
}
3 changes: 3 additions & 0 deletions webapp/src/components/organisms/Header/Header.component.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { ProfileCard } from '../ProfileCard'
import { LanguageSelect } from '../../atoms/LanguageSelect/LanguageSelect.component';

export function Header(){
return (
<header>
<ProfileCard />
<LanguageSelect />
</header>
);
}
8 changes: 3 additions & 5 deletions webapp/src/factories/createArticleRepository.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { ArticleRepository } from "../data";
import { ArticleFileSystemRepository } from "../infra";

export function createArticleRepository(): ArticleRepository {
return new ArticleFileSystemRepository();
}

export const articleRepository = createArticleRepository();
export function createArticleRepository(language: string): ArticleRepository {
return new ArticleFileSystemRepository(language);
}
14 changes: 10 additions & 4 deletions webapp/src/infra/ArticleFileSystemRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@ import { ArticleRepository } from "../data";
import { Article, ArticleMetadata } from "../domain";
import frontMatterParser from "gray-matter";

const ARTICLE_FILE_PATH = path.resolve(process.cwd(), "../posts/src/posts/en");
const ARTICLE_FILE_PATH = path.resolve(process.cwd(), "../posts/src");

export class ArticleFileSystemRepository implements ArticleRepository {
constructor(
private language: string,
private articleFilePath = path.resolve(
ARTICLE_FILE_PATH, language
)
) {}
async findArticleBySlug(slug: string): Promise<Article> {
const filePath = path.join(ARTICLE_FILE_PATH, `${slug}.md`);
const filePath = path.join(this.articleFilePath, `${slug}.md`);
const file = fs.readFileSync(filePath);
const {
data: { title, createdAt, updatedAt, description },
Expand Down Expand Up @@ -37,11 +43,11 @@ export class ArticleFileSystemRepository implements ArticleRepository {
return this.findArticleBySlug(slug).then(
({ content, ...articleMetadata }) => articleMetadata
);
})
});

return Promise.all(articleMetadataPromises);
}
private getArticleFileNames(): string[] {
return fs.readdirSync(ARTICLE_FILE_PATH);
return fs.readdirSync(this.articleFilePath);
}
}
18 changes: 16 additions & 2 deletions webapp/src/pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
import "normalize.css";
import "../components/global/styles/global.css";
import { LocaleContext } from "../components/global";
import { useRouter } from "next/router";
import type { AppProps } from "next/app";

export default function MyApp({ Component, pageProps }) {
return <Component {...pageProps} />;
export default function MyApp({ Component, pageProps }: AppProps) {
const router = useRouter();
const { pathname, asPath, query, locale } = router;

const changeLocale = (nextLocale) => {
router.push({ pathname, query }, asPath, { locale: nextLocale });
};

return (
<LocaleContext.Provider value={{ locale: locale, changeLocale }}>
<Component {...pageProps} />
</LocaleContext.Provider>
);
}
20 changes: 13 additions & 7 deletions webapp/src/pages/articles/[slug].tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { GetStaticProps, GetStaticPaths } from "next";
import { ArticlePage } from "../../components/templates/ArticlePage";
import { Markdown } from "../../components/atoms/Markdown";
import { formatDate } from "../../components/global/formatDate";
import { ArticleRepository } from "../../data";
import { ArticleFileSystemRepository } from "../../infra";
import { formatDate, setLocale } from "../../components/global/formatDate";
import { createArticleRepository } from "../../factories";

export interface ArticleProps {
content: string;
Expand All @@ -25,11 +24,13 @@ export default function Article({
);
}

let articleRepository: ArticleRepository = new ArticleFileSystemRepository();

export const getStaticProps: GetStaticProps<ArticleProps> = async ({
params,
locale
}) => {
setLocale(locale);

const articleRepository = createArticleRepository(locale);
const slug = params?.slug as string;
const article = await articleRepository.findArticleBySlug(slug);
return {
Expand All @@ -41,10 +42,15 @@ export const getStaticProps: GetStaticProps<ArticleProps> = async ({
};
};

export const getStaticPaths: GetStaticPaths = async () => {
export const getStaticPaths: GetStaticPaths = async ({ locales }) => {
const articleRepository = createArticleRepository("en-US");
const articleSlugs = await articleRepository.getAllArticleSlugs();

const paths = articleSlugs.map((slug) => ({ params: { slug } }));
const pathsByLocale = locales.map((locale) => paths.map((path) => ({...path, locale})));

return {
paths: articleSlugs.map((slug) => ({ params: { slug } })),
paths: pathsByLocale.flat(),
fallback: false,
};
};
9 changes: 6 additions & 3 deletions webapp/src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import { GetStaticProps } from "next";
import { formatDate } from "../components/global/formatDate";
import { articleRepository } from "../factories";
import { formatDate, setLocale } from "../components/global/formatDate";
import { HomePage, HomePageProps } from "../components/templates/HomePage";
import { createArticleRepository } from "../factories";

function Home(props: HomePageProps) {
return <HomePage {...props} />;
}

export const getStaticProps: GetStaticProps<HomePageProps> = async () => {
export const getStaticProps: GetStaticProps<HomePageProps> = async ({ locale }) => {
setLocale(locale);

const articleRepository = createArticleRepository(locale);
const articles = await articleRepository.getAllArticleMetadata();
return {
props: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { ArticleRepository } from "../../../src/data";
import { ArticleFileSystemRepository } from "../../../src/infra/ArticleFileSystemRepository";

describe("infra :: ArticleFileSystemRepository", () => {
const articleFileSystemRepository: ArticleRepository = new ArticleFileSystemRepository();
const articleFileSystemRepository: ArticleRepository = new ArticleFileSystemRepository("en-US");
it("should find an article by slug with success", async () => {
const article = await articleFileSystemRepository.findArticleBySlug('testing-in-front-end')
expect(article.slug).toBe("testing-in-front-end");
Expand Down

1 comment on commit bb08a8b

@vercel
Copy link

@vercel vercel bot commented on bb08a8b Nov 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.