Skip to content

Commit

Permalink
Test/add tests (#215)
Browse files Browse the repository at this point in the history
* test: add getCopyrightYearString tests

* ci: add tests on CI

* ci: fix action names

* test: add 404-page tests

* test: add blog page tests

* test: fix next page error

vercel/next.js#34249

* test: add PostReaction tests

* test: 404 page rm beforeAll render page

* test: add test view-counter

* test: fix 404 page

* feat: update introducing article

* feat: add lint to test action
  • Loading branch information
Shramkoweb committed Aug 28, 2022
1 parent 7afb6a5 commit 171f2e5
Show file tree
Hide file tree
Showing 15 changed files with 311 additions and 34 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Lint
name: Linting
on:
- push
- pull_request
Expand Down
22 changes: 22 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Tests
on:
- push
- pull_request
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [ 16.x ]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: npm install, build
run: npm ci
env:
CI: true
- name: run tests
run: npm run test:ci
34 changes: 34 additions & 0 deletions __tests__/404.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { render, screen } from '@testing-library/react';
import * as Sentry from '@sentry/nextjs';

import NotFoundPage from '@/pages/404';

jest.mock('@sentry/nextjs');

describe('404 page', () => {
afterEach(() => {
jest.clearAllMocks();
});

test('renders with correct heading', () => {
render(<NotFoundPage />);

const heading = screen.getByRole('heading', {
name: "YOU'RE IN THE WRONG PLACE",
});

expect(heading).toBeInTheDocument();
});

test('send sentry error on render', () => {
render(<NotFoundPage />);

expect(Sentry.captureException).toHaveBeenCalledWith(new Error('404'));
});

test('send sentry error only on initial render', () => {
render(<NotFoundPage />);

expect(Sentry.captureException).toBeCalledTimes(1);
});
});
75 changes: 75 additions & 0 deletions __tests__/blog.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { render, screen, fireEvent } from '@testing-library/react';

import BlogPage from '@/pages/blog';
import { Post } from '@/lib/types';

jest.mock('@/lib/posts/api');

const posts: Post[] = [
{
data: {
slug: 'first-article',
description: 'First article description',
featured: true,
readTime: '1',
tags: ['JS', 'React'],
title: 'First article title',
createDate: 11,
updateData: 12,
},
content: 'First article long text',
},
{
data: {
slug: 'second-article',
description: 'Second article description',
featured: false,
readTime: '2',
tags: ['TS', 'Redux'],
title: 'Second article title',
createDate: 124,
updateData: 123,
},
content: 'Second article text',
},
];

describe('Blog Page', () => {
test('renders with correct heading', () => {
render(<BlogPage posts={posts} postsAmount={posts.length} />);
const heading = screen.getByRole('heading', {
name: 'Blog',
});

expect(heading).toBeInTheDocument();
});

test('renders with initial posts', () => {
render(<BlogPage posts={posts} postsAmount={posts.length} />);
const blogPostsLinks = screen.getAllByRole('link');

expect(blogPostsLinks).toHaveLength(posts.length);
});

test('on search correct filtered exist posts', () => {
render(<BlogPage posts={posts} postsAmount={posts.length} />);
const inputElement = screen.getByLabelText('Search articles');

fireEvent.change(inputElement, { target: { value: posts[0].data.title } });
const post = screen.getByRole('heading', {
name: posts[0].data.title,
});

expect(post).toBeInTheDocument();
});

test('on search render "No posts found" if unknown post', () => {
render(<BlogPage posts={posts} postsAmount={posts.length} />);
const inputElement = screen.getByLabelText('Search articles');

fireEvent.change(inputElement, { target: { value: 'Unknown post' } });
const post = screen.getByText(/No posts found/);

expect(post).toBeInTheDocument();
});
});
60 changes: 52 additions & 8 deletions _posts/introducing-the-new-shramko.dev.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Introducing the new shramko.dev
description: How I built a modern portfolio in 2022
createDate: 2022-08-13T13:31:25.041Z
updateData: 2022-08-14T08:22:34.069Z
updateData: 2022-08-28T19:37:43.285Z
tags: [Website Redesign, Next.JS, React, Tailwind, Developer Portfolio, Portfolio, Website]
featured: true
---
Expand All @@ -27,19 +27,19 @@ T=0.11 s (779.3 files/s, 159045.1 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
JSON 6 0 0 14734
TypeScript 55 182 11 1809
XML 9 0 0 270
Markdown 5 82 0 190
JavaScript 3 6 2 178
JSON 6 0 0 29012
XML 14 0 0 7191
TypeScript 63 240 14 2240
Markdown 7 213 0 661
JavaScript 5 10 6 201
CSS 2 35 1 165
YAML 2 0 0 44
Bourne Shell 3 9 0 35
YAML 1 0 0 22
SVG 1 0 0 17
Properties 1 0 0 4
Text 1 0 0 3
-------------------------------------------------------------------------------
SUM: 87 314 14 17427
SUM: 105 507 21 39573
-------------------------------------------------------------------------------

```
Expand Down Expand Up @@ -68,6 +68,8 @@ Here are the primary technologies used in this project:
any project you plan to maintain)
- [Prisma](https://www.prisma.io): TypeScript ORM with Zero-Cost Type Safety
- [SWR](https://swr.vercel.app/): Cool stale-while-revalidate hook
- [Jest](https://jestjs.io/): JavaScript Testing Framework
- [Testing Library](https://testing-library.com/): Simple and complete testing utilities
- [Tailwind CSS](https://tailwindcss.com): Utility classes for
consistent/maintainable styling
- [Postcss](https://postcss.org/): CSS processor (pretty much just use it for
Expand Down Expand Up @@ -235,6 +237,48 @@ const views = await prisma.views.upsert({
});
```

## Testing
As a developer, you know how important tests are for any production-level project.
Writing tests takes some time, but they will help you in the long run to solve problems in the codebase.

I also [integrate these tests](https://github.com/Shramkoweb/Portfolio/blob/develop/.github/workflows/tests.yml) into GitHub Actions, so that whenever I deploy to production or make a pull request, tests will run automatically.

My code coverage on 28 August:

```shell
------------------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------------------------|---------|----------|---------|---------|-------------------
All files | 79.52 | 73.33 | 71.42 | 81.98 |
components/blog-post-preview | 100 | 50 | 100 | 100 |
blog-post-preview.tsx | 100 | 50 | 100 | 100 | 29
index.ts | 100 | 100 | 100 | 100 |
components/footer | 100 | 100 | 100 | 100 |
get-copyright.ts | 100 | 100 | 100 | 100 |
components/post-reaction | 96.77 | 80 | 100 | 96.66 |
post-reaction.tsx | 100 | 100 | 100 | 100 |
use-feedback-reducer.ts | 93.33 | 66.66 | 100 | 92.85 | 18
components/view-counter | 100 | 50 | 100 | 100 |
view-counter.tsx | 100 | 50 | 100 | 100 | 25
lib | 100 | 100 | 100 | 100 |
fetcher.ts | 100 | 100 | 100 | 100 |
ga.ts | 100 | 100 | 100 | 100 |
types.ts | 100 | 100 | 100 | 100 |
lib/posts | 34.37 | 0 | 23.07 | 42.3 |
api.ts | 34.61 | 0 | 33.33 | 37.5 | 13-48,53-58,63-65
utils.ts | 33.33 | 100 | 0 | 100 |
pages | 85.18 | 100 | 80 | 83.33 |
404.tsx | 100 | 100 | 100 | 100 |
blog.tsx | 77.77 | 100 | 71.42 | 75 | 101-105
------------------------------|---------|----------|---------|---------|-------------------

Test Suites: 5 passed, 5 total
Tests: 13 passed, 13 total
Snapshots: 0 total
Time: 2.23 s
Ran all test suites.
```

## Next.js

Next's framework allows you to build scalable, performant React code without the configuration hassle.
Expand Down
28 changes: 15 additions & 13 deletions components/footer/footer.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import Link from 'next/link';

import { YEAR_OF_CREATE } from '@/lib/constants';

import { FooterLink } from '@/components/footer-link';
import { getCopyright } from '@/components/footer/get-copyright';
import { getCopyrightYearString } from '@/components/footer/get-copyright';

export function Footer() {
return (
Expand All @@ -10,9 +12,7 @@ export function Footer() {
<div className="w-full max-w-2xl grid grid-cols-1 gap-4 pb-12 sm:grid-cols-3">
<div className="flex flex-col space-y-4">
<Link href="/">
<a
className="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white transition-colors"
>
<a className="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white transition-colors">
Home
</a>
</Link>
Expand All @@ -29,20 +29,21 @@ export function Footer() {
</div>
<div className="flex flex-col space-y-4">
<FooterLink href="https://github.com/shramkoweb">GitHub</FooterLink>
<FooterLink href="https://www.linkedin.com/in/shramko-dev">LinkedIn</FooterLink>
<FooterLink href="https://www.instagram.com/shramko.serhii">Instagram</FooterLink>
<FooterLink href="https://www.linkedin.com/in/shramko-dev">
LinkedIn
</FooterLink>
<FooterLink href="https://www.instagram.com/shramko.serhii">
Instagram
</FooterLink>
</div>
<div className="flex flex-col space-y-4">
<Link href="/gear">
<a className="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white transition-colors">
My
Gear
My Gear
</a>
</Link>
<Link href="/dashboard">
<a
className="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white transition-colors"
>
<a className="text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-white transition-colors">
Dashboard
</a>
</Link>
Expand All @@ -51,9 +52,10 @@ export function Footer() {
<small className="text-gray-600 dark:text-gray-400 pb-4">
© Made with ❤️ in
{' '}
{getCopyright()}
{getCopyrightYearString(YEAR_OF_CREATE, new Date().getFullYear())}
{' '}
by Serhii Shramko
by
Serhii Shramko
</small>
</footer>
);
Expand Down
21 changes: 21 additions & 0 deletions components/footer/get-copyright.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { getCopyrightYearString } from '@/components/footer/get-copyright';

describe('getCopyrightYearString', () => {
test('Return current year if now is 2022', () => {
const currentYear = 2022;
const createYear = 2022;

const result = getCopyrightYearString(createYear, currentYear);

expect(result).toStrictEqual('2022');
});

test('Return string range between create and current', () => {
const currentYear = 2022;
const createYear = 2020;

const result = getCopyrightYearString(createYear, currentYear);

expect(result).toStrictEqual(`${createYear} - ${currentYear}`);
});
});
13 changes: 6 additions & 7 deletions components/footer/get-copyright.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
const YEAR_OF_CREATE = 2022;

export const getCopyright = () => {
const currentYear = new Date().getFullYear();

if (YEAR_OF_CREATE === currentYear) {
export const getCopyrightYearString = (
createYear: number,
currentYear: number,
) => {
if (createYear === currentYear) {
return currentYear.toString();
}

return `${YEAR_OF_CREATE} - ${currentYear}`;
return `${createYear} - ${currentYear}`;
};
51 changes: 51 additions & 0 deletions components/post-reaction/post-reaction.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { render, screen, fireEvent } from '@testing-library/react';

import { PostReaction } from '@/components/post-reaction/post-reaction';

describe('PostReaction component', () => {
test('Render with initial state', () => {
render(<PostReaction />);

const title = screen.getByRole('heading', {
name: 'Was this article helpful ?',
});
const description = screen.getByText('Help me improve my blog');

expect(title).toBeInTheDocument();
expect(description).toBeInTheDocument();
});

describe('reactions click:', () => {
test('worthless', () => {
window.gtag = jest.fn();
render(<PostReaction />);

const worthlessReactionButton = screen.getByRole('button', {
name: 'No',
});
fireEvent.click(worthlessReactionButton);

expect(window.gtag).toHaveBeenCalledWith('event', 'Reaction click', {
event_category: 'Blog - article',
event_label: 'No',
value: undefined,
});
});

test('helpful', () => {
window.gtag = jest.fn();
render(<PostReaction />);

const worthlessReactionButton = screen.getByRole('button', {
name: 'Yes',
});
fireEvent.click(worthlessReactionButton);

expect(window.gtag).toHaveBeenCalledWith('event', 'Reaction click', {
event_category: 'Blog - article',
event_label: 'Yes',
value: undefined,
});
});
});
});
Loading

0 comments on commit 171f2e5

Please sign in to comment.