-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from Rue-pro/feat-select-languages
Feat select languages
- Loading branch information
Showing
22 changed files
with
536 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,17 +1,27 @@ | ||
import { RenderOptions, render } from '@testing-library/preact' | ||
import { | ||
RenderOptions, | ||
renderHook as baseRenderHook, | ||
render, | ||
} from '@testing-library/preact' | ||
import { ComponentChildren } from 'preact' | ||
|
||
const AllTheProviders = ({ children }: { children: ComponentChildren }) => { | ||
return children | ||
return <>{children}</> | ||
} | ||
|
||
const customRender = ( | ||
ui: ComponentChildren, | ||
options?: Omit<RenderOptions, 'queries'>, | ||
) => render(ui, { wrapper: AllTheProviders, ...options }) | ||
|
||
// re-export everything | ||
export * from '@testing-library/preact' | ||
|
||
// override render method | ||
export { customRender as render } | ||
|
||
export const renderHook = <Result, Props>( | ||
hook: (initialProps: Props) => Result, | ||
) => baseRenderHook(hook, { wrapper: AllTheProviders }) | ||
|
||
export const INVALID_JSON_STRING = `{"name": "Joe", "age": null]` | ||
export const CIRCULAR_VALUE = { prop: 'value', circularRef: {} } | ||
CIRCULAR_VALUE.circularRef = CIRCULAR_VALUE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './model' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export type { TLanguageCode, ILanguage } from './types' | ||
export { LANGUAGES } from './languages' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { ILanguage } from '.' | ||
|
||
export const languageCodes = ['en', 'jp', 'pt', 'ko'] as const | ||
|
||
export const LANGUAGES: ILanguage[] = [ | ||
{ label: 'English', value: 'en' }, | ||
{ label: 'Japanese', value: 'jp' }, | ||
{ label: 'Portuguese', value: 'pt' }, | ||
{ label: 'Korean', value: 'ko' }, | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { languageCodes } from './languages' | ||
|
||
export type TLanguageCode = (typeof languageCodes)[number] | ||
export interface ILanguage { | ||
label: string | ||
value: TLanguageCode | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './model' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { browser } from '@shared/browser' | ||
import { TResult } from '@shared/libs/operationResult' | ||
|
||
export class Storage<StorageValue> { | ||
key: string | ||
defaultValue: StorageValue | ||
|
||
constructor(key: string, defaultValue: StorageValue) { | ||
this.key = key | ||
this.defaultValue = defaultValue | ||
} | ||
|
||
get() { | ||
return browser.storage.local.get<StorageValue>(this.key, this.defaultValue) | ||
} | ||
|
||
set(value: StorageValue) { | ||
return browser.storage.local.set<StorageValue>(this.key, value) | ||
} | ||
|
||
onChanged( | ||
callback: ( | ||
changes: TResult<{ | ||
newValue: StorageValue | ||
oldValue: StorageValue | ||
}>, | ||
) => void, | ||
) { | ||
browser.storage.local.onChanged<StorageValue>( | ||
this.key, | ||
callback, | ||
this.defaultValue, | ||
) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export { Storage } from './Storage' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from './ui' |
12 changes: 12 additions & 0 deletions
12
src/features/language/SelectLanguages/model/__mock__/store.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { MockedFunction, vi } from 'vitest' | ||
|
||
import { checkIsSelected, commit, reset, toggle } from '../store' | ||
|
||
export const commitMock: MockedFunction<typeof commit> = vi.fn() | ||
|
||
export const resetMock: MockedFunction<typeof reset> = vi.fn() | ||
|
||
export const toggleMock: MockedFunction<typeof toggle> = vi.fn() | ||
|
||
export const checkIsSelectedMock: MockedFunction<typeof checkIsSelected> = | ||
vi.fn() |
121 changes: 121 additions & 0 deletions
121
src/features/language/SelectLanguages/model/__tests__/store.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
import { allTasks, cleanStores, keepMount } from 'nanostores' | ||
import { afterEach, describe, expect, test, vi } from 'vitest' | ||
|
||
import { Result } from '@shared/libs/operationResult' | ||
import { getErrorToastMock } from '@shared/ui/Toast/helpers/__mock__/getErrorToast' | ||
import { addToastMock } from '@shared/ui/Toast/model/__mock__/store' | ||
|
||
import { waitFor } from '@tests/testUtils' | ||
|
||
import { | ||
$selectedLanguages, | ||
SelectedLanguagesStorage, | ||
checkIsSelected, | ||
commit, | ||
reset, | ||
toggle, | ||
} from '../store' | ||
|
||
describe('selectedLanguages store', () => { | ||
const error = { | ||
type: `ERROR`, | ||
error: null, | ||
} | ||
|
||
afterEach(async () => { | ||
await chrome.storage.local.clear() | ||
cleanStores($selectedLanguages) | ||
$selectedLanguages.set([]) | ||
}) | ||
|
||
test('should initialize with empty array', () => { | ||
keepMount($selectedLanguages) | ||
|
||
expect($selectedLanguages.get()).toEqual([]) | ||
expect(getErrorToastMock).toBeCalledTimes(0) | ||
}) | ||
|
||
describe('commmon', () => { | ||
test('should set value from SelectedLanguagesStorage', async () => { | ||
await SelectedLanguagesStorage.set(['en']) | ||
|
||
keepMount($selectedLanguages) | ||
await allTasks() | ||
|
||
expect($selectedLanguages.get()).toEqual(['en']) | ||
expect(getErrorToastMock).toBeCalledTimes(0) | ||
}) | ||
|
||
test('should set empty array and show toast with error when SelectedLanguagesStorage returns error', async () => { | ||
vi.spyOn(SelectedLanguagesStorage, 'get').mockResolvedValueOnce( | ||
Result.Error(error), | ||
) | ||
|
||
keepMount($selectedLanguages) | ||
await allTasks() | ||
|
||
expect($selectedLanguages.get()).toEqual([]) | ||
expect(getErrorToastMock).toBeCalledWith(error) | ||
}) | ||
}) | ||
|
||
describe('toggle', () => { | ||
test('should select language if not selected and reset', async () => { | ||
await SelectedLanguagesStorage.set([]) | ||
|
||
keepMount($selectedLanguages) | ||
await allTasks() | ||
|
||
toggle('en') | ||
expect(checkIsSelected('en')).toBeTruthy() | ||
|
||
reset() | ||
expect(checkIsSelected('en')).toBeFalsy() | ||
}) | ||
|
||
test('should unselect language if selected and reset', async () => { | ||
await SelectedLanguagesStorage.set(['en']) | ||
|
||
keepMount($selectedLanguages) | ||
await allTasks() | ||
|
||
toggle('en') | ||
expect(checkIsSelected('en')).toBeFalsy() | ||
|
||
reset() | ||
expect(checkIsSelected('en')).toBeTruthy() | ||
}) | ||
}) | ||
|
||
describe('commit', () => { | ||
test('should set to selected languages store selected values and show opration success information', async () => { | ||
$selectedLanguages.set(['en']) | ||
|
||
keepMount($selectedLanguages) | ||
await allTasks() | ||
|
||
waitFor(async () => { | ||
await commit() | ||
|
||
const call = addToastMock.mock.calls[0] | ||
expect(call[0].type).toBe('success') | ||
expect(call[0].title).toBeDefined() | ||
}) | ||
}) | ||
|
||
test('should show error when can not update languages', async () => { | ||
vi.spyOn(SelectedLanguagesStorage, 'set').mockResolvedValue( | ||
Result.Error(error), | ||
) | ||
|
||
keepMount($selectedLanguages) | ||
await allTasks() | ||
|
||
waitFor(async () => { | ||
await commit() | ||
|
||
expect(getErrorToastMock).toBeCalledWith(error) | ||
}) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
import { atom, onMount, task } from 'nanostores' | ||
|
||
import { TLanguageCode } from '@entities/language' | ||
import { Storage } from '@entities/storage' | ||
|
||
import { browser } from '@shared/browser' | ||
import { addToast, getErrorToast } from '@shared/ui/Toast' | ||
|
||
export const SelectedLanguagesStorage = new Storage<TLanguageCode[]>( | ||
'selected_languages', | ||
[], | ||
) | ||
|
||
let defaultValue: TLanguageCode[] = [] | ||
export const $selectedLanguages = atom<TLanguageCode[]>([]) | ||
|
||
onMount($selectedLanguages, () => { | ||
task(async () => { | ||
const getResult = await SelectedLanguagesStorage.get() | ||
if (getResult.data) { | ||
defaultValue = getResult.data | ||
$selectedLanguages.set(getResult.data) | ||
} else { | ||
addToast(getErrorToast(getResult.error)) | ||
} | ||
}) | ||
}) | ||
|
||
export const toggle = async (languageCode: TLanguageCode) => { | ||
const isSelected = $selectedLanguages.get().includes(languageCode) | ||
|
||
const newSelectedLanguages = isSelected | ||
? $selectedLanguages.get().filter((lang) => lang !== languageCode) | ||
: [...$selectedLanguages.get(), languageCode] | ||
|
||
$selectedLanguages.set(newSelectedLanguages) | ||
} | ||
|
||
export const checkIsSelected = (languageCode: TLanguageCode) => { | ||
return $selectedLanguages.get().includes(languageCode) | ||
} | ||
|
||
export const reset = () => { | ||
$selectedLanguages.set(defaultValue) | ||
} | ||
|
||
export const commit = async () => { | ||
const setResult = await SelectedLanguagesStorage.set($selectedLanguages.get()) | ||
|
||
setResult.data | ||
? addToast({ | ||
type: 'success', | ||
title: browser.i18n.getMessage('SELECTED_LANGUAGES_SAVED'), | ||
}) | ||
: addToast(getErrorToast(setResult.error)) | ||
} |
Oops, something went wrong.