diff --git a/.github/workflows/ci-frontend-size-limit.yml b/.github/workflows/ci-frontend-size-limit.yml
index fe5073aa..7e06b489 100644
--- a/.github/workflows/ci-frontend-size-limit.yml
+++ b/.github/workflows/ci-frontend-size-limit.yml
@@ -13,8 +13,18 @@ jobs:
env:
CI_JOB_NUMBER: 1
steps:
- - uses: actions/checkout@v1
- - uses: andresz1/size-limit-action@v1
+ - name: Checkout
+ uses: actions/checkout@v1
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v2.0.1
+ id: pnpm-install
+ with:
+ version: 7
+ run_install: false
+
+ - name: Check bundle size
+ uses: andresz1/size-limit-action@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
directory: frontend
diff --git a/.github/workflows/ci-frontend.yml b/.github/workflows/ci-frontend.yml
index f68b733c..77b82b79 100644
--- a/.github/workflows/ci-frontend.yml
+++ b/.github/workflows/ci-frontend.yml
@@ -22,22 +22,40 @@ jobs:
node: [16.15.1]
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v1
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ - name: Install node
+ uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
- - run: npm ci --loglevel warn
+ - name: Install pnpm
+ uses: pnpm/action-setup@v2.0.1
+ id: pnpm-install
+ with:
+ version: 7
+ run_install: false
+
+ - name: Get pnpm store directory
+ id: pnpm-cache
+ run: |
+ echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
+
+ - name: Setup pnpm cache
+ uses: actions/cache@v3
+ with:
+ path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
+ key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+ restore-keys: |
+ ${{ runner.os }}-pnpm-store-
+
+ - name: Install dependencies
+ run: pnpm i --loglevel warn
working-directory: ./frontend
- - uses: actions/cache@v2
- with:
- path: ${{ github.workspace }}/frontend/node_modules/.cache
- key: ${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
- restore-keys: |
- ${{ runner.os }}-node-
-
- - run: npm run check:translation
+ - name: Run translations check
+ run: pnpm run check:translation
working-directory: ./frontend
check-typescript:
@@ -47,22 +65,40 @@ jobs:
node: [16.15.1]
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v1
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ - name: Install node
+ uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
- - run: npm ci --loglevel warn
+ - name: Install pnpm
+ uses: pnpm/action-setup@v2.0.1
+ id: pnpm-install
+ with:
+ version: 7
+ run_install: false
+
+ - name: Get pnpm store directory
+ id: pnpm-cache
+ run: |
+ echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
+
+ - name: Setup pnpm cache
+ uses: actions/cache@v3
+ with:
+ path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
+ key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+ restore-keys: |
+ ${{ runner.os }}-pnpm-store-
+
+ - name: Install dependencies
+ run: pnpm i --loglevel warn
working-directory: ./frontend
- - uses: actions/cache@v2
- with:
- path: ${{ github.workspace }}/frontend/node_modules/.cache
- key: ${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
- restore-keys: |
- ${{ runner.os }}-node-
-
- - run: npm run check:types
+ - name: Run type check
+ run: pnpm run check:types
working-directory: ./frontend
lint:
@@ -72,15 +108,40 @@ jobs:
node: [16.15.1]
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v1
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ - name: Install node
+ uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
- - run: npm ci --loglevel warn
+ - name: Install pnpm
+ uses: pnpm/action-setup@v2.0.1
+ id: pnpm-install
+ with:
+ version: 7
+ run_install: false
+
+ - name: Get pnpm store directory
+ id: pnpm-cache
+ run: |
+ echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
+
+ - name: Setup pnpm cache
+ uses: actions/cache@v3
+ with:
+ path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
+ key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+ restore-keys: |
+ ${{ runner.os }}-pnpm-store-
+
+ - name: Install dependencies
+ run: pnpm i --loglevel warn
working-directory: ./frontend
- - run: npm run lint
+ - name: Run linters
+ run: pnpm run lint
working-directory: ./frontend
test:
@@ -90,18 +151,43 @@ jobs:
node: [16.15.1]
steps:
- - uses: actions/checkout@v2
- - uses: actions/setup-node@v1
+ - name: Checkout
+ uses: actions/checkout@v2
+
+ - name: Install node
+ uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
- - run: npm ci --loglevel warn
+ - name: Install pnpm
+ uses: pnpm/action-setup@v2.0.1
+ id: pnpm-install
+ with:
+ version: 7
+ run_install: false
+
+ - name: Get pnpm store directory
+ id: pnpm-cache
+ run: |
+ echo "::set-output name=pnpm_cache_dir::$(pnpm store path)"
+
+ - name: Setup pnpm cache
+ uses: actions/cache@v3
+ with:
+ path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
+ key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
+ restore-keys: |
+ ${{ runner.os }}-pnpm-store-
+
+ - name: Install dependencies
+ run: pnpm i --loglevel warn
working-directory: ./frontend
- - run: npm run test:coverage
+ - name: Collect tests coverage
+ run: pnpm run test --coverage
working-directory: ./frontend
- - name: submit coverage
- run: node ${{ github.workspace }}/frontend/node_modules/.bin/codecov
+ - name: Submit coverage
+ run: ${{ github.workspace }}/frontend/node_modules/.bin/codecov
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
diff --git a/Dockerfile b/Dockerfile
index ff82d499..191cda9b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -45,9 +45,9 @@ RUN if [ -z "$SKIP_FRONTEND_BUILD" ] ; then \
ADD frontend/package.json /srv/frontend/package.json
ADD frontend/package-lock.json /srv/frontend/package-lock.json
WORKDIR /srv/frontend
-RUN mkdir node_modules
RUN if [ -z "$SKIP_FRONTEND_BUILD" ] ; then \
- CI=true npm ci --loglevel warn \
+ npm i -g pnpm && \
+ CI=true pnpm i --loglevel warn \
else \
echo "skip frontend build" \
; fi
diff --git a/Dockerfile.artifacts b/Dockerfile.artifacts
index d7e60d40..49bb6fd7 100644
--- a/Dockerfile.artifacts
+++ b/Dockerfile.artifacts
@@ -3,11 +3,15 @@ FROM node:16.15.1-alpine as build-frontend-deps
ARG CI
ENV SKIP_FRONTEND_TEST=true
+ENV CI=true
-RUN apk add --no-cache --update git
ADD frontend/package.json /srv/frontend/package.json
ADD frontend/package-lock.json /srv/frontend/package-lock.json
-RUN cd /srv/frontend && CI=true npm ci
+RUN
+ cd /srv/frontend && \
+ apk add --no-cache --update git && \
+ npm i -g pnpm && \
+ pnpm i
FROM node:16.15.1-alpine as build-frontend
diff --git a/frontend/app/components/auth/auth.spec.tsx b/frontend/app/components/auth/auth.spec.tsx
index 130068ae..0bfb905f 100644
--- a/frontend/app/components/auth/auth.spec.tsx
+++ b/frontend/app/components/auth/auth.spec.tsx
@@ -173,7 +173,7 @@ describe('', () => {
it('should show validation error for token', async () => {
StaticStore.config.auth_providers = ['email'];
- jest.spyOn(api, 'emailSignin').mockImplementationOnce(async () => null);
+ const emailSignin = jest.spyOn(api, 'emailSignin').mockImplementationOnce(async () => null);
const { getByText, getByTitle, getByPlaceholderText } = render();
@@ -183,7 +183,7 @@ describe('', () => {
target: { value: 'email@email.com' },
});
fireEvent.click(getByText('Submit'));
- await waitFor(() => expect(api.emailSignin).toBeCalled());
+ await waitFor(() => expect(emailSignin).toBeCalled());
expect(getByText('Back')).toHaveClass('auth-back-button');
expect(getByTitle('Close sign-in dropdown')).toHaveClass('auth-close-button');
diff --git a/frontend/app/components/comment-form/comment-form.spec.tsx b/frontend/app/components/comment-form/comment-form.spec.tsx
index 822dad52..7418477f 100644
--- a/frontend/app/components/comment-form/comment-form.spec.tsx
+++ b/frontend/app/components/comment-form/comment-form.spec.tsx
@@ -31,7 +31,7 @@ function setup(overrideProps: Partial = {}, overrideConfig: Partial ;
return render();
diff --git a/frontend/app/components/profile/profile.spec.tsx b/frontend/app/components/profile/profile.spec.tsx
index 8c3c23da..42f6f662 100644
--- a/frontend/app/components/profile/profile.spec.tsx
+++ b/frontend/app/components/profile/profile.spec.tsx
@@ -1,6 +1,5 @@
import '@testing-library/jest-dom';
-import { waitFor } from '@testing-library/preact';
-import { fireEvent } from '@testing-library/dom';
+import { waitFor, fireEvent } from '@testing-library/preact';
import { render } from 'tests/utils';
import * as api from 'common/api';
diff --git a/frontend/app/components/select/select.spec.tsx b/frontend/app/components/select/select.spec.tsx
index 839e115b..9b5f9f2b 100644
--- a/frontend/app/components/select/select.spec.tsx
+++ b/frontend/app/components/select/select.spec.tsx
@@ -1,5 +1,5 @@
import '@testing-library/jest-dom';
-import { fireEvent } from '@testing-library/preact';
+import { fireEvent, screen, waitFor } from '@testing-library/preact';
import { render } from 'tests/utils';
import { Select } from './select';
@@ -14,32 +14,32 @@ const items = [
describe('', () => {
it('should has static class names', () => {
- const { container } = render();
- const selectElement = container.querySelector('.select-element');
-
- expect(container.querySelector('.select')).toBeInTheDocument();
- expect(container.querySelector('.select-arrow')).toBeInTheDocument();
- expect(selectElement).toBeInTheDocument();
- fireEvent.focus(selectElement as HTMLSelectElement);
- expect(container.querySelector('.select_focused')).toBeInTheDocument();
+ render();
+ expect(screen.getByRole('combobox')).toHaveClass('select-element');
+ expect(screen.getByTestId('select-root')).toHaveClass('select');
+ expect(screen.getByTestId('select-arrow')).toHaveClass('select-arrow');
});
it('should render selected item', () => {
- const { container, getAllByText } = render();
- const selectedOption = container.querySelector('option');
+ render();
- expect(getAllByText(items[0].label)).toHaveLength(2);
+ const selectedItem = items[0];
+ const selectedOption = screen.getAllByRole('option')[0];
+
+ expect(screen.getAllByText(selectedItem.label)).toHaveLength(2);
expect(selectedOption).toBeInTheDocument();
- expect(selectedOption?.selected).toBeTruthy();
- expect(selectedOption?.textContent).toBe(items[0].label);
+ expect(selectedOption.selected).toBeTruthy();
+ expect(selectedOption.textContent).toBe(selectedItem.label);
});
it('should highlight select on focus', async () => {
- const { container } = render();
- const select = container.querySelector('select');
+ render();
- expect(container.querySelector('select')).toBeInTheDocument();
- fireEvent.focus(select as HTMLSelectElement);
- expect(container.querySelector('.rootFocused')).toBeInTheDocument();
+ fireEvent.focus(screen.getByRole('combobox'));
+ await waitFor(() => {
+ const rootElement = screen.getByTestId('select-root');
+ expect(rootElement).toHaveClass('select_focused');
+ expect(rootElement).toHaveClass('rootFocused');
+ });
});
});
diff --git a/frontend/app/components/select/select.tsx b/frontend/app/components/select/select.tsx
index 3a33943c..56ad5889 100644
--- a/frontend/app/components/select/select.tsx
+++ b/frontend/app/components/select/select.tsx
@@ -31,6 +31,7 @@ export function Select({ items, selected, size = 'md', ...props }: Props) {
return (
{selectedItem.label}
-
+