Skip to content

Commit

Permalink
Merge pull request #25 from VKCOM/public
Browse files Browse the repository at this point in the history
Release v2.2.0
  • Loading branch information
mclaran authored Aug 8, 2024
2 parents 2652b5f + ea390a6 commit 78b0053
Show file tree
Hide file tree
Showing 83 changed files with 1,808 additions and 375 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Change Log

## 2.2.0

### Новое
- Добавлена возможность открыть авторизацию в новом окне для всех модулей.
- Добавлена возможность менять текста в [кнопке One Tap](https://id.vk.com/business/go/docs/ru/vkid/latest/vk-id/connection/web/onetap).
- Добавлен новый сценарий "Быстрая регистрация" в [шторку авторизации](https://id.vk.com/business/go/docs/ru/vkid/latest/vk-id/connection/web/auth).

## 2.1.0

### Новое
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

---

ℹ️ Версия VK ID SDK 2.1.0 поддерживает авторизацию по протоколу [OAuth 2.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-10), а также способы входа через аккаунты Одноклассников и Mail.ru.
ℹ️ Версия VK ID SDK 2.2.0 поддерживает авторизацию по протоколу [OAuth 2.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-10), а также способы входа через аккаунты Одноклассников и Mail.ru.

---

Expand Down Expand Up @@ -114,7 +114,7 @@ if (container) {
- [Что такое VK ID](https://id.vk.com/about/business/go/docs/ru/vkid/latest/vk-id/intro/start-page)
- [Создание приложения](https://id.vk.com/about/business/go/docs/ru/vkid/latest/vk-id/connection/create-application)
- [Требования к дизайну](https://id.vk.com/about/business/go/docs/ru/vkid/latest/vk-id/connection/guidelines/design-rules-oauth)
- [Спецификация](https://vkcom.github.io/vkid-web-sdk/)
- [Спецификация](https://vkcom.github.io/vkid-web-sdk/docs)

## Contributing

Expand Down
30 changes: 21 additions & 9 deletions __tests__/auth/auth.tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { encodeStatsInfo } from '#/utils/url';

import { version } from '../../package.json';
import { WINDOW_LOCATION_HOST } from '../constants';
import { wait } from '../utils';

const APP_ID = 100;

Expand Down Expand Up @@ -34,7 +35,7 @@ describe('Auth', () => {
});

beforeEach(() => {
Config.init({ app: APP_ID, redirectUrl: 'https://id.vk.com', state: 'state', codeVerifier: 'codeVerifier', mode: ConfigAuthMode.Redirect });
Config.init({ app: APP_ID, redirectUrl: 'https://id.vk.com', state: 'state', codeVerifier: 'codeVerifier', mode: ConfigAuthMode.InNewTab });
reporter
.addLabel('layer', 'unit')
.feature('Units')
Expand All @@ -46,7 +47,10 @@ describe('Auth', () => {
});

test('Should redirect to url with default fields', async () => {
await Auth.login();
Config.update({ mode: ConfigAuthMode.Redirect });

void Auth.login();
await wait(100);
expect(assignFn).toHaveBeenCalled();

const callArgs: string[] = assignFn.mock.calls[0];
Expand All @@ -73,14 +77,17 @@ describe('Auth', () => {
});

test('Should redirect to url with additional fields', async () => {
Config.update({ mode: ConfigAuthMode.Redirect });

const params: AuthParams = {
scheme: Scheme.LIGHT,
lang: Languages.RUS,
screen: 'phone',
statsFlowSource: AuthStatsFlowSource.BUTTON_ONE_TAP,
};

await Auth.login(params);
void Auth.login(params);
await wait(100);
expect(assignFn).toHaveBeenCalled();

const callArgs: string[] = assignFn.mock.calls[0];
Expand Down Expand Up @@ -112,7 +119,6 @@ describe('Auth', () => {
});

test('Opens a window with default fields', () => {
Config.update({ mode: ConfigAuthMode.InNewTab });
Auth.login().catch(console.error);
expect(openFn).toHaveBeenCalled();

Expand All @@ -130,6 +136,7 @@ describe('Auth', () => {
expect(searchParams.get('app_id')).toEqual(APP_ID.toString()),
expect(searchParams.get('redirect_uri')).toEqual(Config.get().redirectUrl),
expect(searchParams.get('prompt')).toEqual(''),
expect(searchParams.get('origin')).toEqual(location.protocol + '//' + location.hostname),
expect(searchParams.get('stats_info')).toEqual(encodeStatsInfo({
flow_source: AuthStatsFlowSource.AUTH,
session_id: 'abc',
Expand All @@ -140,7 +147,7 @@ describe('Auth', () => {
});

test('Opens a window with additional fields', () => {
Config.update({ mode: ConfigAuthMode.InNewTab, prompt: [Prompt.Login, Prompt.Consent] });
Config.update({ prompt: [Prompt.Login, Prompt.Consent] });
const params: AuthParams = {
scheme: Scheme.LIGHT,
lang: Languages.RUS,
Expand All @@ -165,6 +172,7 @@ describe('Auth', () => {
expect(searchParams.get('app_id')).toEqual(APP_ID.toString()),
expect(searchParams.get('redirect_uri')).toEqual(Config.get().redirectUrl),
expect(searchParams.get('prompt')).toEqual([Prompt.Login, Prompt.Consent].join(' ').trim()),
expect(searchParams.get('origin')).toEqual(location.protocol + '//' + location.hostname),
expect(searchParams.get('stats_info')).toEqual(encodeStatsInfo({
flow_source: AuthStatsFlowSource.AUTH,
session_id: 'abc',
Expand All @@ -175,7 +183,7 @@ describe('Auth', () => {
});

test('Must redirect with payload', async () => {
Config.update({ mode: ConfigAuthMode.InNewTab, redirectUrl: 'https://id.vk.com?query=123' });
Config.update({ redirectUrl: 'https://id.vk.com?query=123' });

const response: AuthResponse = {
code: 'code',
Expand All @@ -200,7 +208,7 @@ describe('Auth', () => {
});
});

await Auth.login();
void Auth.login();
expect(openFn).toHaveBeenCalled();
expect(closeFn).toHaveBeenCalled();
expect(assignFn).toHaveBeenCalled();
Expand All @@ -221,7 +229,8 @@ describe('Auth', () => {
});

test('Should fetch oauth2/auth with authorization_code exchange params', async () => {
await Auth.login();
void Auth.login();

const { redirectUrl, app, codeVerifier, state } = Config.get();
const mockResponse = { json() {
return Promise.resolve(JSON.parse('{ "state": "state" }'));
Expand All @@ -248,10 +257,13 @@ describe('Auth', () => {
});

test('Should set state and codeVerifier params to cookie after login()', async () => {
Config.update({ mode: ConfigAuthMode.Redirect });

codeVerifierCookie('1');
stateCookie('1');

await Auth.login();
void Auth.login();

const { codeVerifier, state } = Config.get();

expect(codeVerifier).toEqual(codeVerifierCookie());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('FloatingOneTapStatsCollector', () => {
});

it('Log ScreenProcessed', async () => {
void statsCollector.sendScreenProcessed({
void statsCollector.sendScreenProceed({
lang: Languages.RUS,
scheme: Scheme.DARK,
contentId: FloatingOneTapContentId.SIGN_IN_TO_ACCOUNT,
Expand Down Expand Up @@ -79,6 +79,7 @@ describe('FloatingOneTapStatsCollector', () => {
name: 'text_type',
value: TEXT_TYPE[FloatingOneTapContentId.SIGN_IN_TO_ACCOUNT],
}],

},
} }));
});
Expand Down
29 changes: 22 additions & 7 deletions __tests__/widgets/oneTap/analytics/OneTapStatsCollector.tests.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import {
ProductionStatsEventScreen,
RegistrationStatsEventParams,
ProductionStatsEventTypes,
ProductionStatsTypeActions,
} from '#/core/analytics';
import { ProductionStatsEventScreen, ProductionStatsEventTypes, ProductionStatsTypeActions, RegistrationStatsEventParams } from '#/core/analytics';
import { Config } from '#/core/config';
import { Languages, Scheme } from '#/types';
import { request } from '#/utils/request';
import { OneTapContentId, OneTapSkin } from '#/widgets/oneTap';
import { OneTapStatsCollector } from '#/widgets/oneTap/analytics';
import { TEXT_TYPE } from '#/widgets/oneTap/analytics/constants';

import { wait } from '../../../utils';

Expand Down Expand Up @@ -124,7 +122,12 @@ describe('OneTapStatsCollector', () => {
});

it('Log ScreenProceed', async () => {
void statsCollector.sendScreenProceed();
void statsCollector.sendScreenProceed({
lang: Languages.RUS,
scheme: Scheme.DARK,
skin: OneTapSkin.Secondary,
contentId: OneTapContentId.CALCULATE,
});
await wait(0);

const events = JSON.parse((requestMocked.mock.lastCall?.[1] as any).events)[0];
Expand All @@ -138,6 +141,18 @@ describe('OneTapStatsCollector', () => {
}, {
name: 'unique_session_id',
value: 'id',
}, {
name: 'theme_type',
value: Scheme.DARK,
}, {
name: 'style_type',
value: OneTapSkin.Secondary,
}, {
name: 'language',
value: Languages.RUS.toString(),
}, {
name: 'text_type',
value: TEXT_TYPE[OneTapContentId.CALCULATE],
}],
})));
});
Expand Down
15 changes: 3 additions & 12 deletions __tests__/widgets/oneTap/oneTap.tests.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { BRIDGE_MESSAGE_TYPE_SDK } from '#/core/bridge/bridge';
import { WidgetEvents } from '#/core/widget';
import { Config, Languages, OAuthName, OneTapSkin } from '#/index';
import { Config, Languages, OAuthName, OneTapContentId, OneTapSkin } from '#/index';
import { Scheme } from '#/types';
import { OneTap } from '#/widgets/oneTap';
import { OneTapBridgeMessage } from '#/widgets/oneTap/types';
Expand Down Expand Up @@ -39,6 +39,7 @@ describe('OneTap', () => {
oneTap = new TestOneTap();

container = document.createElement('div', {});

document.body.append(container);

reporter
Expand Down Expand Up @@ -83,6 +84,7 @@ describe('OneTap', () => {
expect(searchParams.get('redirect_uri')).toEqual('test'),
expect(searchParams.get('oauth_version')).toEqual('2'),
expect(searchParams.get('uuid')).toEqual('abc'),
expect(searchParams.get('content_id')).toEqual(`${OneTapContentId.SIGN_IN}`),
];

expect([...new Set(searchParams.keys())].length).toEqual(expectArr.length);
Expand Down Expand Up @@ -196,17 +198,6 @@ describe('OneTap', () => {
expect(oneTapEl?.getAttribute('data-state')).toEqual('loaded');
});

test('Must render oauthlist if oauthList param exists', async () => {
oneTap.render({
container,
oauthList: [OAuthName.MAIL, OAuthName.OK],
});
await wait(0);
const oneTapEl = document.querySelector('[data-test-id="oneTap"]');
const oauthListEl = oneTapEl?.querySelector('[data-test-id="oauthList"]');
expect(oauthListEl).toBeTruthy();
});

test('Must not render oauthlist if only OAuthName.VK', async () => {
oneTap.render({
container,
Expand Down
8 changes: 5 additions & 3 deletions demo/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ function handleSelectParamsChange() {

document.querySelector('html')?.setAttribute('data-scheme', demoStore.scheme);
}
['lang', 'scheme', 'contentId', 'oauthes', 'onetapSkin', 'fastAuthEnabledOnetap', 'fastAuthEnabledFloatingOnetap'].forEach((item) => {
['lang', 'scheme', 'floatingOneTapContentId', 'buttonOneTapContentId', 'oauthes', 'onetapSkin', 'fastAuthEnabledOnetap', 'fastAuthEnabledFloatingOnetap'].forEach((item) => {
document.getElementById(item)?.addEventListener('change', handleSelectParamsChange);
});

Expand All @@ -101,8 +101,10 @@ function handleConfigParamsChange() {
demoStore = Object.assign(demoStore, { [this.name]: this.value });
saveDemoStoreInLS(demoStore);
}
const modeEl = document.getElementById('mode');
modeEl && modeEl.addEventListener('change', handleConfigParamsChange);
['mode', 'responseMode'].forEach((name) => {
const modeEl = document.getElementById(name);
modeEl && modeEl.addEventListener('change', handleConfigParamsChange);
});

function handleModuleEnabledCheckboxChange() {
demoStore = Object.assign(demoStore, { [this.name]: this.checked });
Expand Down
7 changes: 5 additions & 2 deletions demo/types.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import { TokenResult } from '#/auth/types';
import { ConfigAuthMode, Prompt } from '#/core/config';
import { ConfigAuthMode, ConfigResponseMode, Prompt } from '#/core/config';
import { Languages, Scheme } from '#/types';
import { FloatingOneTapContentId } from '#/widgets/floatingOneTap';
import { OneTapContentId } from '#/widgets/oneTap';

export interface DemoStore {
app: number;
state: string;
codeVerifier: string;
codeChallenge: string;
contentId: FloatingOneTapContentId;
floatingOneTapContentId: FloatingOneTapContentId;
buttonOneTapContentId: OneTapContentId;
lang: Languages;
scheme: Scheme;
onetapSkin: 'primary' | 'secondary';
oauthes: '' | 'mail_ru' |'ok_ru' | 'mail_ru,ok_ru';
mode: ConfigAuthMode;
responseMode: ConfigResponseMode;
enable_oauthList: boolean;
enable_basicAuth: boolean;
enable_oneTap: boolean;
Expand Down
23 changes: 14 additions & 9 deletions demo/utils/createModule.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import * as VKID from '#/index';
import { OAuthName } from '#/index';
import { OneTapInternalEvents } from '#/widgets/oneTap/events';
import * as VKID from '@vkid/sdk';

import { showAuthInfoSnackbar, showInitErrorSnackbar } from '#demo/components/snackbar';
import { DemoStore } from '#demo/types';
import { handleCallbackAuth } from '#demo/utils/handleAuth';

export const createOneTap = (demoStore: DemoStore) => {
const container = document.getElementById('oneTap') as HTMLElement;
Expand All @@ -14,13 +13,15 @@ export const createOneTap = (demoStore: DemoStore) => {
lang: Number(demoStore.lang),
scheme: demoStore.scheme,
skin: demoStore.onetapSkin as VKID.OneTapParams['skin'],
oauthList: demoStore.oauthes ? demoStore.oauthes.split(',') as OAuthName[] : undefined,
oauthList: demoStore.oauthes ? demoStore.oauthes.split(',') as VKID.OAuthName[] : undefined,
fastAuthEnabled: !!demoStore.fastAuthEnabledOnetap,
contentId: Number(demoStore.buttonOneTapContentId),
};

const oneTap = new VKID.OneTap();
oneTap.on(VKID.WidgetEvents.ERROR, showInitErrorSnackbar)
.on(OneTapInternalEvents.AUTHENTICATION_INFO, showAuthInfoSnackbar)
.on(VKID.OneTapInternalEvents.LOGIN_SUCCESS, handleCallbackAuth)
.on(VKID.OneTapInternalEvents.AUTHENTICATION_INFO, showAuthInfoSnackbar)
.render(params);

return oneTap;
Expand All @@ -30,15 +31,17 @@ export const createFloatingOneTap = (demoStore: DemoStore) => {
const params = {
appName: 'VK ID Demo',
showAlternativeLogin: true,
contentId: Number(demoStore.contentId),
lang: Number(demoStore.lang),
scheme: demoStore.scheme,
oauthList: demoStore.oauthes ? demoStore.oauthes.split(',') as OAuthName[] : undefined,
oauthList: demoStore.oauthes ? demoStore.oauthes.split(',') as VKID.OAuthName[] : undefined,
fastAuthEnabled: !!demoStore.fastAuthEnabledFloatingOnetap,
contentId: Number(demoStore.floatingOneTapContentId),
};

const floatingOneTap = new VKID.FloatingOneTap();
floatingOneTap.on(VKID.WidgetEvents.ERROR, showInitErrorSnackbar)
floatingOneTap
.on(VKID.FloatingOneTapInternalEvents.LOGIN_SUCCESS, handleCallbackAuth)
.on(VKID.WidgetEvents.ERROR, showInitErrorSnackbar)
.render(params);

return floatingOneTap;
Expand All @@ -48,7 +51,9 @@ export const createOAuthList = (demoStore: DemoStore) => {
const container = document.getElementById('oauthList') as HTMLElement;

const oauthList = new VKID.OAuthList();
oauthList.on(VKID.WidgetEvents.ERROR, showInitErrorSnackbar)
oauthList
.on(VKID.OAuthListInternalEvents.LOGIN_SUCCESS, handleCallbackAuth)
.on(VKID.WidgetEvents.ERROR, showInitErrorSnackbar)
.render({
container,
scheme: demoStore.scheme,
Expand Down
Loading

0 comments on commit 78b0053

Please sign in to comment.