diff --git a/src/renderer/test/utils/common.test.ts b/src/renderer/test/utils/common.test.ts index 2996fa6..e57ba24 100644 --- a/src/renderer/test/utils/common.test.ts +++ b/src/renderer/test/utils/common.test.ts @@ -1,13 +1,4 @@ -import { - expect, - test, - describe, - afterEach, - vi, - beforeEach, - afterAll, - beforeAll, -} from 'vitest' +import { expect, test, describe, vi } from 'vitest' import { resizeImage, formatDate, @@ -15,6 +6,7 @@ import { cacheCoverColor, calcCoverColor, getCoverColor, + storage, } from '@/renderer/utils/common' import { IpcChannels } from '@/main/IpcChannelsName' import { APIs } from '@/main/CacheAPIsName' @@ -183,3 +175,28 @@ describe('getCoverColor', () => { vi.stubGlobal('ipcRenderer', undefined) }) }) + +test('storage', () => { + const mockLocalStorage: any = { + test: { + key: 'value', + }, + } + + vi.stubGlobal('localStorage', { + getItem: (key: string) => { + return mockLocalStorage[key] ?? undefined + }, + setItem: (key: string, value: string) => { + expect(key).toBe('test') + expect(value).toEqual(`{"key":"value2"}`) + mockLocalStorage[key] = value + }, + }) + + expect(storage.set('test', { key: 'value2' })).toBe(undefined) + expect(storage.get('test')).toEqual({ key: 'value2' }) + expect(storage.get('test2')).toBe(null) + + vi.stubGlobal('localStorage', undefined) +}) diff --git a/src/renderer/test/utils/cookie.test.ts b/src/renderer/test/utils/cookie.test.ts new file mode 100644 index 0000000..fd20716 --- /dev/null +++ b/src/renderer/test/utils/cookie.test.ts @@ -0,0 +1,185 @@ +import { expect, test, describe, vi, beforeEach, afterEach } from 'vitest' + +import { + setCookies, + getCookie, + removeCookie, + parseCookies, + removeAllCookies, +} from '@/renderer/utils/cookie' +import Cookies from 'js-cookie' + +describe('parseCookies', () => { + test('parse simple cookies', () => { + expect(parseCookies('test=test; test2=test2')).toEqual([ + { + key: 'test', + value: 'test', + }, + { + key: 'test2', + value: 'test2', + }, + ]) + }) + + test('parse cookies with empty value, expires and double semicolon', () => { + expect( + parseCookies( + 'test=; test2=test2; Expires=Wed, 21 Oct 2015 07:28:00 GMT;;' + ) + ).toEqual([ + { + key: 'test', + value: '', + }, + { + key: 'test2', + value: 'test2', + }, + ]) + }) + + test('prase cookies with max-age', () => { + expect(parseCookies('test2=test2; Max-Age=604800')).toEqual([ + { + key: 'test2', + value: 'test2', + options: { + expires: 7, + }, + }, + ]) + }) + + test('parse cookies with invalid max-age', () => { + expect(parseCookies('test2=test2; Max-Age=invalid')).toEqual([ + { + key: 'test2', + value: 'test2', + }, + ]) + }) + + test('parse cookie with path', () => { + expect(parseCookies('test2=test2; Path=/')).toEqual([ + { + key: 'test2', + value: 'test2', + options: { + path: '/', + }, + }, + ]) + }) + + test('parse cookie with HTTPOnly', () => { + expect(parseCookies('test2=test2; HttpOnly')).toEqual([ + { + key: 'test2', + value: 'test2', + }, + ]) + }) +}) + +describe('setCookies', () => { + beforeEach(() => { + removeAllCookies() + }) + + afterEach(() => { + removeAllCookies() + }) + + test('set one cookie', () => { + const setSpy = vi.spyOn(Cookies, 'set') + expect(setCookies('test=test')).toBe(undefined) + expect(setSpy).toHaveBeenCalledTimes(1) + expect(Cookies.get('test')).toBe('test') + }) + + test('set multiple cookies', () => { + const setSpy = vi.spyOn(Cookies, 'set') + expect(setCookies('test=test; test2=test2;')).toBe(undefined) + expect(setSpy).toHaveBeenCalledTimes(2) + expect(Cookies.get('test')).toBe('test') + expect(Cookies.get('test2')).toBe('test2') + }) + + test('set Netease qr code login cookies', () => { + const setSpy = vi.spyOn(Cookies, 'set') + expect( + setCookies( + 'MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/eapi/clientlog; HTTPOnly;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/api/clientlog; HTTPOnly;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/openapi/clientlog; HTTPOnly;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/api/feedback; HTTPOnly;MUSIC_SNS=; Max-Age=0; Expires=Thu, 14 Apr 2022 08:25:58 GMT; Path=/;MUSIC_U=a0d026bc508b3b4427c06d7c2aa66c200764eceb4734513f6caf9308528ac1369f00530f; Max-Age=15552000; Expires=Tue, 11 Oct 2022 08:25:58 GMT; Path=/; HTTPOnly;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/weapi/clientlog; HTTPOnly;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/wapi/clientlog; HTTPOnly;__csrf=5b947e26d720eeaf28700786d41ea5dd; Max-Age=1296010; Expires=Fri, 29 Apr 2022 08:26:08 GMT; Path=/;;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/weapi/feedback; HTTPOnly;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/neapi/feedback; HTTPOnly;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/weapi/clientlog; HTTPOnly;MUSIC_A_T=; Max-Age=0; Expires=Thu, 14 Apr 2022 08:25:58 GMT; Path=/;;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/wapi/feedback; HTTPOnly;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/neapi/feedback; HTTPOnly;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/api/clientlog; HTTPOnly;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/api/feedback; HTTPOnly;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/weapi/feedback; HTTPOnly;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/neapi/clientlog; HTTPOnly;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/eapi/feedback; HTTPOnly;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/neapi/clientlog; HTTPOnly;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/eapi/clientlog; HTTPOnly;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/wapi/feedback; HTTPOnly;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/openapi/clientlog; HTTPOnly;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/wapi/clientlog; HTTPOnly;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:40:05 GMT; Path=/eapi/feedback; HTTPOnly' + ) + ).toBe(undefined) + expect(setSpy).toHaveBeenCalledTimes(26) + expect(Cookies.get('MUSIC_U')).toBe( + 'a0d026bc508b3b4427c06d7c2aa66c200764eceb4734513f6caf9308528ac1369f00530f' + ) + expect(Cookies.get('__csrf')).toBe('5b947e26d720eeaf28700786d41ea5dd') + expect(Cookies.get('MUSIC_R_T')).toBe(undefined) // because of path is not / + expect(Cookies.get('MUSIC_A_T')).toBe(undefined) // because of path is not / + }) + + test('set Netease email login cookies', () => { + const setSpy = vi.spyOn(Cookies, 'set') + expect( + setCookies( + '__remember_me=true; Max-Age=1296000; Expires=Fri, 29 Apr 2022 08:41:59 GMT; Path=/;;MUSIC_A_T=1519475931796; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/wapi/clientlog;;MUSIC_A_T=; Max-Age=0; Expires=Thu, 14 Apr 2022 08:41:59 GMT; Path=/;;MUSIC_A_T=1519475931796; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/eapi/clientlog;;MUSIC_A_T=1519475931796; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/neapi/clientlog;;MUSIC_R_T=1519475931800; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/wapi/feedback;;__csrf=be948ee03ab9aa0c4cc0bfd7a6276728; Max-Age=1296010; Expires=Fri, 29 Apr 2022 08:42:09 GMT; Path=/;;MUSIC_R_T=1519475931800; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/openapi/clientlog;;MUSIC_U=1239b6c1217d8cd240df9c8fa15e99a69c3fa0e6a9dfac6b5b7c2a817be2b9a20807b5c6b97f6b5; Max-Age=1296000; Expires=Fri, 29 Apr 2022 08:41:59 GMT; Path=/;;MUSIC_R_T=1519475931800; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/eapi/feedback;;MUSIC_R_T=1519475931800; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/eapi/clientlog;;MUSIC_R_T=1519475931800; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/wapi/clientlog;;MUSIC_A_T=1519475931796; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/api/feedback;;MUSIC_R_T=1519475931800; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/api/clientlog;;MUSIC_SNS=; Max-Age=0; Expires=Thu, 14 Apr 2022 08:41:59 GMT; Path=/;MUSIC_A_T=1519475931796; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/eapi/feedback;;MUSIC_R_T=1519475931800; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/weapi/clientlog;;MUSIC_A_T=1519475931796; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/weapi/clientlog;;MUSIC_A_T=1519475931796; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/openapi/clientlog;;MUSIC_R_T=1519475931800; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/weapi/feedback;;MUSIC_R_T=; Max-Age=0; Expires=Thu, 14 Apr 2022 08:41:59 GMT; Path=/;;MUSIC_R_T=1519475931800; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/neapi/feedback;;MUSIC_A_T=1519475931796; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/weapi/feedback;;MUSIC_A_T=1519475931796; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/neapi/feedback;;MUSIC_R_T=1519475931800; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/api/feedback;;MUSIC_A_T=1519475931796; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/api/clientlog;;MUSIC_R_T=1519475931800; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/neapi/clientlog;;MUSIC_A_T=1519475931796; Max-Age=2147483647; Expires=Tue, 2 May 2090 11:56:06 GMT; Path=/wapi/feedback;' + ) + ).toBe(undefined) + expect(setSpy).toHaveBeenCalledTimes(28) + expect(Cookies.get('MUSIC_U')).toBe( + '1239b6c1217d8cd240df9c8fa15e99a69c3fa0e6a9dfac6b5b7c2a817be2b9a20807b5c6b97f6b5' + ) + expect(Cookies.get('__remember_me')).toBe('true') + expect(Cookies.get('__csrf')).toBe('be948ee03ab9aa0c4cc0bfd7a6276728') + expect(Cookies.get('MUSIC_R_T')).toBe(undefined) // because of path is not / + expect(Cookies.get('MUSIC_A_T')).toBe(undefined) // because of path is not / + }) + + test('set Netease phone login cookies', () => { + const setSpy = vi.spyOn(Cookies, 'set') + expect( + setCookies( + 'MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/openapi/clientlog;;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/eapi/feedback;;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/weapi/clientlog;;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/weapi/feedback;;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/wapi/clientlog;;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/api/feedback;;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/wapi/feedback;;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/api/clientlog;;NMTID=00OEhPKoaEluGvd9kgutai-iADpQkEAAAGAJz_PTQ; Max-Age=315360000; Expires=Sun, 11 Apr 2032 08:45:34 GMT; Path=/;;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/eapi/clientlog;;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/api/clientlog;;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/openapi/clientlog;;MUSIC_U=a0d026bc508b3b4427c06d7c2aa66c204ba2a162576a513197f262a1067ea44651; Max-Age=1296000; Expires=Fri, 29 Apr 2022 08:45:34 GMT; Path=/;;MUSIC_SNS=; Max-Age=0; Expires=Thu, 14 Apr 2022 08:45:34 GMT; Path=/;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/wapi/clientlog;;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/eapi/clientlog;;__csrf=78328f711c179391b096a67ad9d0f08b; Max-Age=1296010; Expires=Fri, 29 Apr 2022 08:45:44 GMT; Path=/;;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/neapi/feedback;;__remember_me=true; Max-Age=1296000; Expires=Fri, 29 Apr 2022 08:45:34 GMT; Path=/;;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/weapi/clientlog;;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/eapi/feedback;;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/api/feedback;;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/weapi/feedback;;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/neapi/clientlog;;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/neapi/feedback;;MUSIC_A_T=1376792466000; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/neapi/clientlog;;MUSIC_R_T=1376792525993; Max-Age=2147483647; Expires=Tue, 02 May 2090 11:59:41 GMT; Path=/wapi/feedback;' + ) + ).toBe(undefined) + expect(setSpy).toHaveBeenCalledTimes(27) + expect(Cookies.get('MUSIC_U')).toBe( + 'a0d026bc508b3b4427c06d7c2aa66c204ba2a162576a513197f262a1067ea44651' + ) + expect(Cookies.get('__remember_me')).toBe('true') + expect(Cookies.get('__csrf')).toBe('78328f711c179391b096a67ad9d0f08b') + expect(Cookies.get('NMTID')).toBe( + '00OEhPKoaEluGvd9kgutai-iADpQkEAAAGAJz_PTQ' + ) + expect(Cookies.get('MUSIC_R_T')).toBe(undefined) // because of path is not / + expect(Cookies.get('MUSIC_A_T')).toBe(undefined) // because of path is not / + }) +}) + +test('getCookie', () => { + removeAllCookies() + document.cookie = 'test=test' + expect(getCookie('test')).toBe('test') + removeAllCookies() +}) + +test('removeCookie', () => { + removeAllCookies() + document.cookie = 'test=test' + removeCookie('test') + expect(getCookie('test')).toBe(undefined) + expect(document.cookie).toBe('') + removeAllCookies() +}) + +test('removeAllCookies', () => { + document.cookie = 'test=test' + document.cookie = 'test2=test2' + removeAllCookies() + expect(document.cookie).toBe('') +}) diff --git a/src/renderer/utils/common.ts b/src/renderer/utils/common.ts index 318609e..871aa85 100644 --- a/src/renderer/utils/common.ts +++ b/src/renderer/utils/common.ts @@ -101,8 +101,6 @@ export function formatDuration( }` : `${mins} ${units[locale].mins}` } - - return String(milliseconds) } export function scrollToTop(smooth = false) { diff --git a/src/renderer/utils/cookie.ts b/src/renderer/utils/cookie.ts index b87851d..8ca0974 100644 --- a/src/renderer/utils/cookie.ts +++ b/src/renderer/utils/cookie.ts @@ -1,12 +1,63 @@ -import Cookies from 'js-cookie' +import Cookies, { CookieAttributes } from 'js-cookie' + +interface Cookie { + key: string + value: string + options?: CookieAttributes +} + +export function parseCookies(cookie: string): Cookie[] { + const cookies: Cookie[] = [] + let tmpCookie: Cookie | null = null + cookie.split(';').forEach(item => { + const splittedItem = item.split('=') + if (splittedItem.length !== 2) return + + const key = splittedItem[0].trim() + const value = splittedItem[1].trim() + + if (key.toLowerCase() === 'expires') return + if (key.toLowerCase() === 'max-age') { + const expires = Number(value) / 60 / 60 / 24 + if (isNaN(expires)) return + tmpCookie = { + ...((tmpCookie as Cookie) ?? {}), + options: { + ...tmpCookie?.options, + expires: expires, + }, + } + return + } + + if (key.toLowerCase() === 'path') { + tmpCookie = { + ...((tmpCookie as Cookie) ?? {}), + options: { + ...tmpCookie?.options, + path: value, + }, + } + return + } + + if (tmpCookie) cookies.push(tmpCookie) + + tmpCookie = { + key, + value, + } + }) + + if (tmpCookie) cookies.push(tmpCookie) + + return cookies +} export function setCookies(string: string) { - const cookies = string.replace(/;.*?HTTPOnly.*?;/g, ';;').split(';;') - cookies.map(cookie => { - const cookieKeyValue = cookie.split(';')[0].split('=') - const [key, value] = cookieKeyValue - // store.account.cookies[key] = value - Cookies.set(key, value, { expires: 3650 }) + const cookies = parseCookies(string) + cookies.forEach(cookie => { + Cookies.set(cookie.key, cookie.value, cookie.options) }) } @@ -17,3 +68,13 @@ export function getCookie(key: string) { export function removeCookie(key: string) { Cookies.remove(key) } + +export function removeAllCookies() { + const cookies = document.cookie.split(';') + + cookies.forEach(cookie => { + const splitted = cookie.split('=') + const name = splitted[0].trim() + document.cookie = `${name}=;max-age=0` + }) +}