Merge branch 'master' into codex/tiaoyitiao
This commit is contained in:
@@ -46,6 +46,8 @@ Genarrative / 陶泥儿是一个 AI 原生互动内容与小游戏平台。当
|
|||||||
3. 登录弹窗继续复用现有独立 modal 和页签结构,不在页面中新增功能说明类文案,也不把邀请码输入放回登录面板。
|
3. 登录弹窗继续复用现有独立 modal 和页签结构,不在页面中新增功能说明类文案,也不把邀请码输入放回登录面板。
|
||||||
4. 微信小程序 `web-view` 外壳默认不预登录,首次进入直接打开 H5,并保持与 Web 端一致的未登录状态;只有 H5 触发 `openLoginModal` / `requireAuth` 等受保护入口时,才跳转小程序原生授权态。
|
4. 微信小程序 `web-view` 外壳默认不预登录,首次进入直接打开 H5,并保持与 Web 端一致的未登录状态;只有 H5 触发 `openLoginModal` / `requireAuth` 等受保护入口时,才跳转小程序原生授权态。
|
||||||
5. 小程序内需要登录时不展示 H5 登录弹窗,也不走手输手机号 / 短信验证码流程;统一通过原生 `button open-type="getPhoneNumber"` 获取微信手机号授权,再调用 `/api/auth/wechat/miniprogram-login` 与 `/api/auth/wechat/bind-phone` 换取系统登录态。
|
5. 小程序内需要登录时不展示 H5 登录弹窗,也不走手输手机号 / 短信验证码流程;统一通过原生 `button open-type="getPhoneNumber"` 获取微信手机号授权,再调用 `/api/auth/wechat/miniprogram-login` 与 `/api/auth/wechat/bind-phone` 换取系统登录态。
|
||||||
|
6. 小程序 `web-view` 页必须启用好友分享与朋友圈分享,分享目标固定回到 `pages/web-view/index`,不把 H5 当前 URL 作为不受控启动参数传回小程序页。
|
||||||
|
7. 小程序 `web-view` 外壳运行时通过 `wx.getAccountInfoSync().miniProgram.envVersion` 自动识别版本:线上版 `release` 使用 `www.genarrative.world`,体验版 `trial` 与开发版 `develop` 使用 `dev.genarrative.world`;传给后端的 `x-mini-program-env` 分别为 `release`、`trial`、`dev`。
|
||||||
|
|
||||||
## 账户与充值
|
## 账户与充值
|
||||||
|
|
||||||
|
|||||||
@@ -2,15 +2,17 @@
|
|||||||
// 示例:https://game.example.com/
|
// 示例:https://game.example.com/
|
||||||
// 注意:必须是 https 域名,不能是 localhost、IP 地址或未备案域名。
|
// 注意:必须是 https 域名,不能是 localhost、IP 地址或未备案域名。
|
||||||
const WEB_VIEW_ENTRY_URL = 'https://www.genarrative.world';
|
const WEB_VIEW_ENTRY_URL = 'https://www.genarrative.world';
|
||||||
|
const DEV_WEB_VIEW_ENTRY_URL = 'https://dev.genarrative.world';
|
||||||
|
|
||||||
// 中文注释:这里填写 Rust api-server 的公网 HTTPS 域名,必须在“小程序后台-开发设置-request 合法域名”中配置。
|
// 中文注释:这里填写 Rust api-server 的公网 HTTPS 域名,必须在“小程序后台-开发设置-request 合法域名”中配置。
|
||||||
// 如果 H5 和 API 同域,可保持和 WEB_VIEW_ENTRY_URL 同一个域名;请求路径会固定走 /api/auth/wechat/miniprogram-login。
|
// 如果 H5 和 API 同域,可保持和 WEB_VIEW_ENTRY_URL 同一个域名;请求路径会固定走 /api/auth/wechat/miniprogram-login。
|
||||||
const API_BASE_URL = 'https://www.genarrative.world';
|
const API_BASE_URL = 'https://www.genarrative.world';
|
||||||
|
const DEV_API_BASE_URL = 'https://dev.genarrative.world';
|
||||||
|
|
||||||
// 中文注释:这里填写微信小程序 AppID,用于后端记录会话来源;project.config.json 里的 appid 也要保持一致。
|
// 中文注释:这里填写微信小程序 AppID,用于后端记录会话来源;project.config.json 里的 appid 也要保持一致。
|
||||||
const MINI_PROGRAM_APP_ID = 'wx3da23ea14ca66b65';
|
const MINI_PROGRAM_APP_ID = 'wx3da23ea14ca66b65';
|
||||||
|
|
||||||
// 中文注释:按当前上传版本填写 develop / trial / release,后端会写入会话来源快照。
|
// 中文注释:仅作为运行时环境识别失败时的兜底;正常情况下由 wx.getAccountInfoSync 自动判断。
|
||||||
const MINI_PROGRAM_ENV = 'release';
|
const MINI_PROGRAM_ENV = 'release';
|
||||||
|
|
||||||
// 中文注释:给 H5 加一个来源标记,便于后续前端或后端识别这是微信小程序 web-view 宿主。
|
// 中文注释:给 H5 加一个来源标记,便于后续前端或后端识别这是微信小程序 web-view 宿主。
|
||||||
@@ -21,6 +23,8 @@ const WEB_VIEW_SOURCE_QUERY = {
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
API_BASE_URL,
|
API_BASE_URL,
|
||||||
|
DEV_API_BASE_URL,
|
||||||
|
DEV_WEB_VIEW_ENTRY_URL,
|
||||||
MINI_PROGRAM_APP_ID,
|
MINI_PROGRAM_APP_ID,
|
||||||
MINI_PROGRAM_ENV,
|
MINI_PROGRAM_ENV,
|
||||||
WEB_VIEW_ENTRY_URL,
|
WEB_VIEW_ENTRY_URL,
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
API_BASE_URL,
|
API_BASE_URL,
|
||||||
|
DEV_API_BASE_URL,
|
||||||
|
DEV_WEB_VIEW_ENTRY_URL,
|
||||||
MINI_PROGRAM_APP_ID,
|
MINI_PROGRAM_APP_ID,
|
||||||
MINI_PROGRAM_ENV,
|
MINI_PROGRAM_ENV,
|
||||||
WEB_VIEW_ENTRY_URL,
|
WEB_VIEW_ENTRY_URL,
|
||||||
@@ -16,6 +18,33 @@ const PAY_RESULT_STORAGE_KEY = 'genarrative:wechat-pay-result';
|
|||||||
const AUTH_RESULT_STORAGE_KEY = 'genarrative:mini-program-auth-result';
|
const AUTH_RESULT_STORAGE_KEY = 'genarrative:mini-program-auth-result';
|
||||||
const AUTH_ACTION_LOGIN = 'login';
|
const AUTH_ACTION_LOGIN = 'login';
|
||||||
const PAY_RESULT_RECHECK_DELAY_MS = 120;
|
const PAY_RESULT_RECHECK_DELAY_MS = 120;
|
||||||
|
const WEB_VIEW_SHARE_TITLE = '陶泥儿';
|
||||||
|
const WEB_VIEW_SHARE_PATH = '/pages/web-view/index';
|
||||||
|
|
||||||
|
function showWebViewShareMenu() {
|
||||||
|
if (typeof wx.showShareMenu !== 'function') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wx.showShareMenu({
|
||||||
|
withShareTicket: true,
|
||||||
|
menus: ['shareAppMessage', 'shareTimeline'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildWebViewShareAppMessage() {
|
||||||
|
return {
|
||||||
|
title: WEB_VIEW_SHARE_TITLE,
|
||||||
|
path: WEB_VIEW_SHARE_PATH,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildWebViewShareTimeline() {
|
||||||
|
return {
|
||||||
|
title: WEB_VIEW_SHARE_TITLE,
|
||||||
|
query: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function isConfiguredEntryUrl(value) {
|
function isConfiguredEntryUrl(value) {
|
||||||
const trimmed = String(value || '').trim();
|
const trimmed = String(value || '').trim();
|
||||||
@@ -78,6 +107,68 @@ function parseBooleanQueryFlag(value) {
|
|||||||
return value === true || value === '1' || value === 'true' || value === 'yes';
|
return value === true || value === '1' || value === 'true' || value === 'yes';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeMiniProgramEnv(value) {
|
||||||
|
const normalized = String(value || '').trim().toLowerCase();
|
||||||
|
if (normalized === 'release') {
|
||||||
|
return 'release';
|
||||||
|
}
|
||||||
|
if (normalized === 'trial') {
|
||||||
|
return 'trial';
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
normalized === 'develop' ||
|
||||||
|
normalized === 'development' ||
|
||||||
|
normalized === 'dev'
|
||||||
|
) {
|
||||||
|
return 'dev';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function readMiniProgramEnvVersion() {
|
||||||
|
if (typeof wx.getAccountInfoSync !== 'function') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const accountInfo = wx.getAccountInfoSync();
|
||||||
|
return (
|
||||||
|
accountInfo &&
|
||||||
|
accountInfo.miniProgram &&
|
||||||
|
accountInfo.miniProgram.envVersion
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('[web-view] read mini program env failed', error);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveMiniProgramRuntimeConfig() {
|
||||||
|
const miniProgramEnv =
|
||||||
|
normalizeMiniProgramEnv(readMiniProgramEnvVersion()) ||
|
||||||
|
normalizeMiniProgramEnv(MINI_PROGRAM_ENV) ||
|
||||||
|
'release';
|
||||||
|
const useReleaseChannel = miniProgramEnv === 'release';
|
||||||
|
const webViewEntryUrl = useReleaseChannel
|
||||||
|
? WEB_VIEW_ENTRY_URL
|
||||||
|
: DEV_WEB_VIEW_ENTRY_URL || WEB_VIEW_ENTRY_URL;
|
||||||
|
const apiBaseUrl = useReleaseChannel
|
||||||
|
? API_BASE_URL
|
||||||
|
: DEV_API_BASE_URL || API_BASE_URL;
|
||||||
|
const sourceQuery = {
|
||||||
|
...WEB_VIEW_SOURCE_QUERY,
|
||||||
|
};
|
||||||
|
if (!useReleaseChannel) {
|
||||||
|
sourceQuery.miniProgramEnv = miniProgramEnv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
apiBaseUrl,
|
||||||
|
miniProgramEnv,
|
||||||
|
sourceQuery,
|
||||||
|
webViewEntryUrl,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function shouldStartAuthFromQuery(query) {
|
function shouldStartAuthFromQuery(query) {
|
||||||
return String((query && query.authAction) || '').trim() === AUTH_ACTION_LOGIN;
|
return String((query && query.authAction) || '').trim() === AUTH_ACTION_LOGIN;
|
||||||
}
|
}
|
||||||
@@ -87,12 +178,13 @@ function shouldReturnToPreviousPage(query) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resolveWebViewUrl(authResult) {
|
function resolveWebViewUrl(authResult) {
|
||||||
const entryUrl = String(WEB_VIEW_ENTRY_URL || '').trim();
|
const runtimeConfig = resolveMiniProgramRuntimeConfig();
|
||||||
|
const entryUrl = String(runtimeConfig.webViewEntryUrl || '').trim();
|
||||||
if (!isConfiguredEntryUrl(entryUrl)) {
|
if (!isConfiguredEntryUrl(entryUrl)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
const sourcedUrl = appendQuery(entryUrl, WEB_VIEW_SOURCE_QUERY);
|
const sourcedUrl = appendQuery(entryUrl, runtimeConfig.sourceQuery);
|
||||||
if (!authResult || !authResult.token) {
|
if (!authResult || !authResult.token) {
|
||||||
return sourcedUrl;
|
return sourcedUrl;
|
||||||
}
|
}
|
||||||
@@ -178,7 +270,8 @@ function wxLogin() {
|
|||||||
|
|
||||||
function requestMiniProgramLogin(code) {
|
function requestMiniProgramLogin(code) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const apiBaseUrl = trimTrailingSlash(API_BASE_URL);
|
const runtimeConfig = resolveMiniProgramRuntimeConfig();
|
||||||
|
const apiBaseUrl = trimTrailingSlash(runtimeConfig.apiBaseUrl);
|
||||||
if (!isConfiguredApiBaseUrl(apiBaseUrl)) {
|
if (!isConfiguredApiBaseUrl(apiBaseUrl)) {
|
||||||
reject(new Error('请先配置 API_BASE_URL'));
|
reject(new Error('请先配置 API_BASE_URL'));
|
||||||
return;
|
return;
|
||||||
@@ -195,7 +288,7 @@ function requestMiniProgramLogin(code) {
|
|||||||
'x-client-platform': resolveClientPlatform(),
|
'x-client-platform': resolveClientPlatform(),
|
||||||
'x-client-instance-id': getClientInstanceId(),
|
'x-client-instance-id': getClientInstanceId(),
|
||||||
'x-mini-program-app-id': MINI_PROGRAM_APP_ID,
|
'x-mini-program-app-id': MINI_PROGRAM_APP_ID,
|
||||||
'x-mini-program-env': MINI_PROGRAM_ENV,
|
'x-mini-program-env': runtimeConfig.miniProgramEnv,
|
||||||
},
|
},
|
||||||
success(response) {
|
success(response) {
|
||||||
if (response.statusCode >= 200 && response.statusCode < 300) {
|
if (response.statusCode >= 200 && response.statusCode < 300) {
|
||||||
@@ -219,7 +312,8 @@ function requestMiniProgramLogin(code) {
|
|||||||
|
|
||||||
function requestMiniProgramBindPhone(authToken, wechatPhoneCode) {
|
function requestMiniProgramBindPhone(authToken, wechatPhoneCode) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const apiBaseUrl = trimTrailingSlash(API_BASE_URL);
|
const runtimeConfig = resolveMiniProgramRuntimeConfig();
|
||||||
|
const apiBaseUrl = trimTrailingSlash(runtimeConfig.apiBaseUrl);
|
||||||
if (!isConfiguredApiBaseUrl(apiBaseUrl)) {
|
if (!isConfiguredApiBaseUrl(apiBaseUrl)) {
|
||||||
reject(new Error('请先配置 API_BASE_URL'));
|
reject(new Error('请先配置 API_BASE_URL'));
|
||||||
return;
|
return;
|
||||||
@@ -237,7 +331,7 @@ function requestMiniProgramBindPhone(authToken, wechatPhoneCode) {
|
|||||||
'x-client-platform': resolveClientPlatform(),
|
'x-client-platform': resolveClientPlatform(),
|
||||||
'x-client-instance-id': getClientInstanceId(),
|
'x-client-instance-id': getClientInstanceId(),
|
||||||
'x-mini-program-app-id': MINI_PROGRAM_APP_ID,
|
'x-mini-program-app-id': MINI_PROGRAM_APP_ID,
|
||||||
'x-mini-program-env': MINI_PROGRAM_ENV,
|
'x-mini-program-env': runtimeConfig.miniProgramEnv,
|
||||||
},
|
},
|
||||||
success(response) {
|
success(response) {
|
||||||
if (response.statusCode >= 200 && response.statusCode < 300) {
|
if (response.statusCode >= 200 && response.statusCode < 300) {
|
||||||
@@ -271,18 +365,6 @@ async function resolveAuthResult() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function refreshMiniProgramSessionSilently() {
|
|
||||||
if (!isConfiguredApiBaseUrl(API_BASE_URL)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return await resolveAuthResult();
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('[web-view] silent mini program login refresh failed', error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
authResult: null,
|
authResult: null,
|
||||||
@@ -296,8 +378,10 @@ Page({
|
|||||||
|
|
||||||
async onLoad(query = {}) {
|
async onLoad(query = {}) {
|
||||||
this._lastLaunchQuery = query;
|
this._lastLaunchQuery = query;
|
||||||
|
showWebViewShareMenu();
|
||||||
|
const runtimeConfig = resolveMiniProgramRuntimeConfig();
|
||||||
// 中文注释:web-view 只能打开已配置业务域名;未配置时展示本地提示,避免空白页误判。
|
// 中文注释:web-view 只能打开已配置业务域名;未配置时展示本地提示,避免空白页误判。
|
||||||
if (!isConfiguredEntryUrl(WEB_VIEW_ENTRY_URL)) {
|
if (!isConfiguredEntryUrl(runtimeConfig.webViewEntryUrl)) {
|
||||||
this.setData({
|
this.setData({
|
||||||
errorMessage: '请先在 miniprogram/config.js 填写 WEB_VIEW_ENTRY_URL。',
|
errorMessage: '请先在 miniprogram/config.js 填写 WEB_VIEW_ENTRY_URL。',
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -309,19 +393,18 @@ Page({
|
|||||||
const forcedPhoneBinding = parseBooleanQueryFlag(query.phoneBindingRequired);
|
const forcedPhoneBinding = parseBooleanQueryFlag(query.phoneBindingRequired);
|
||||||
const returnToPreviousPage = shouldReturnToPreviousPage(query);
|
const returnToPreviousPage = shouldReturnToPreviousPage(query);
|
||||||
if (!shouldStartAuthFromQuery(query) && !forcedPhoneBinding) {
|
if (!shouldStartAuthFromQuery(query) && !forcedPhoneBinding) {
|
||||||
const authResult = await refreshMiniProgramSessionSilently();
|
|
||||||
this.setData({
|
this.setData({
|
||||||
authResult,
|
authResult: null,
|
||||||
errorMessage: '',
|
errorMessage: '',
|
||||||
loading: false,
|
loading: false,
|
||||||
phoneBindingRequired: false,
|
phoneBindingRequired: false,
|
||||||
returnToPreviousPage: false,
|
returnToPreviousPage: false,
|
||||||
webViewUrl: resolveWebViewUrl(authResult),
|
webViewUrl: resolveWebViewUrl(null),
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isConfiguredApiBaseUrl(API_BASE_URL)) {
|
if (!isConfiguredApiBaseUrl(runtimeConfig.apiBaseUrl)) {
|
||||||
this.setData({
|
this.setData({
|
||||||
errorMessage: '请先在 miniprogram/config.js 填写 API_BASE_URL。',
|
errorMessage: '请先在 miniprogram/config.js 填写 API_BASE_URL。',
|
||||||
loading: false,
|
loading: false,
|
||||||
@@ -490,4 +573,12 @@ Page({
|
|||||||
// 中文注释:支付由独立 native 页面承接,web-view 消息只保留调试输出。
|
// 中文注释:支付由独立 native 页面承接,web-view 消息只保留调试输出。
|
||||||
console.info('[web-view] message', event.detail);
|
console.info('[web-view] message', event.detail);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onShareAppMessage() {
|
||||||
|
return buildWebViewShareAppMessage();
|
||||||
|
},
|
||||||
|
|
||||||
|
onShareTimeline() {
|
||||||
|
return buildWebViewShareTimeline();
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,18 +17,24 @@ type MiniProgramPage = {
|
|||||||
data: Record<string, unknown>;
|
data: Record<string, unknown>;
|
||||||
setData: (patch: Record<string, unknown>) => void;
|
setData: (patch: Record<string, unknown>) => void;
|
||||||
onLoad: (query?: Record<string, string>) => Promise<void>;
|
onLoad: (query?: Record<string, string>) => Promise<void>;
|
||||||
|
onShareAppMessage: () => Record<string, unknown>;
|
||||||
|
onShareTimeline: () => Record<string, unknown>;
|
||||||
onShow: () => void;
|
onShow: () => void;
|
||||||
consumePayResult: () => void;
|
consumePayResult: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
function createWxMock() {
|
function createWxMock() {
|
||||||
return {
|
return {
|
||||||
|
getAccountInfoSync: vi.fn(() => ({
|
||||||
|
miniProgram: { envVersion: 'release' },
|
||||||
|
})),
|
||||||
getStorageSync: vi.fn(() => ''),
|
getStorageSync: vi.fn(() => ''),
|
||||||
getSystemInfoSync: vi.fn(() => ({ platform: 'ios' })),
|
getSystemInfoSync: vi.fn(() => ({ platform: 'ios' })),
|
||||||
login: vi.fn(),
|
login: vi.fn(),
|
||||||
navigateBack: vi.fn(),
|
navigateBack: vi.fn(),
|
||||||
removeStorageSync: vi.fn(),
|
removeStorageSync: vi.fn(),
|
||||||
request: vi.fn(),
|
request: vi.fn(),
|
||||||
|
showShareMenu: vi.fn(),
|
||||||
setStorageSync: vi.fn(),
|
setStorageSync: vi.fn(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -54,6 +60,8 @@ function loadWebViewPage(
|
|||||||
if (requestPath === '../../config') {
|
if (requestPath === '../../config') {
|
||||||
return {
|
return {
|
||||||
API_BASE_URL: 'https://www.genarrative.world/',
|
API_BASE_URL: 'https://www.genarrative.world/',
|
||||||
|
DEV_API_BASE_URL: 'https://dev.genarrative.world/',
|
||||||
|
DEV_WEB_VIEW_ENTRY_URL: 'https://dev.genarrative.world/',
|
||||||
MINI_PROGRAM_APP_ID: 'wx-test-app',
|
MINI_PROGRAM_APP_ID: 'wx-test-app',
|
||||||
MINI_PROGRAM_ENV: 'release',
|
MINI_PROGRAM_ENV: 'release',
|
||||||
WEB_VIEW_ENTRY_URL: 'https://www.genarrative.world/',
|
WEB_VIEW_ENTRY_URL: 'https://www.genarrative.world/',
|
||||||
@@ -91,7 +99,7 @@ describe('mini-program web-view auth page', () => {
|
|||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('默认进入时刷新微信小程序登录态后打开 web-view', async () => {
|
test('默认进入时不预登录,直接打开未登录 web-view', async () => {
|
||||||
const wxMock = createWxMock();
|
const wxMock = createWxMock();
|
||||||
wxMock.login.mockImplementation(({ success }) => {
|
wxMock.login.mockImplementation(({ success }) => {
|
||||||
success({ code: 'wx-login-code' });
|
success({ code: 'wx-login-code' });
|
||||||
@@ -109,19 +117,58 @@ describe('mini-program web-view auth page', () => {
|
|||||||
|
|
||||||
await page.onLoad({});
|
await page.onLoad({});
|
||||||
|
|
||||||
expect(wxMock.login).toHaveBeenCalledTimes(1);
|
expect(wxMock.login).not.toHaveBeenCalled();
|
||||||
expect(wxMock.request).toHaveBeenCalledWith(
|
expect(wxMock.request).not.toHaveBeenCalled();
|
||||||
expect.objectContaining({
|
|
||||||
url: 'https://www.genarrative.world/api/auth/wechat/miniprogram-login',
|
|
||||||
method: 'POST',
|
|
||||||
data: { code: 'wx-login-code' },
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
expect(page.data.loading).toBe(false);
|
expect(page.data.loading).toBe(false);
|
||||||
expect(page.data.phoneBindingRequired).toBe(false);
|
expect(page.data.phoneBindingRequired).toBe(false);
|
||||||
expect(page.data.webViewUrl).toBe(
|
expect(page.data.webViewUrl).toBe(
|
||||||
'https://www.genarrative.world/?clientType=mini_program&clientRuntime=wechat_mini_program#auth_provider=wechat&auth_token=jwt-active-wechat&auth_binding_status=active',
|
'https://www.genarrative.world/?clientType=mini_program&clientRuntime=wechat_mini_program',
|
||||||
);
|
);
|
||||||
|
expect(wxMock.showShareMenu).toHaveBeenCalledWith({
|
||||||
|
withShareTicket: true,
|
||||||
|
menus: ['shareAppMessage', 'shareTimeline'],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('默认进入时即便微信新身份待绑手机号,也不弹出绑定手机号页', async () => {
|
||||||
|
const wxMock = createWxMock();
|
||||||
|
wxMock.login.mockImplementation(({ success }) => {
|
||||||
|
success({ code: 'wx-login-code' });
|
||||||
|
});
|
||||||
|
wxMock.request.mockImplementation(({ success }) => {
|
||||||
|
success({
|
||||||
|
statusCode: 200,
|
||||||
|
data: {
|
||||||
|
token: 'jwt-pending-wechat',
|
||||||
|
bindingStatus: 'pending_bind_phone',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const page = loadWebViewPage(wxMock);
|
||||||
|
|
||||||
|
await page.onLoad({});
|
||||||
|
|
||||||
|
expect(wxMock.login).not.toHaveBeenCalled();
|
||||||
|
expect(wxMock.request).not.toHaveBeenCalled();
|
||||||
|
expect(page.data.loading).toBe(false);
|
||||||
|
expect(page.data.phoneBindingRequired).toBe(false);
|
||||||
|
expect(page.data.webViewUrl).toBe(
|
||||||
|
'https://www.genarrative.world/?clientType=mini_program&clientRuntime=wechat_mini_program',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('web-view 页面分享好友和朋友圈都回到小程序 web-view 入口', () => {
|
||||||
|
const wxMock = createWxMock();
|
||||||
|
const page = loadWebViewPage(wxMock);
|
||||||
|
|
||||||
|
expect(page.onShareAppMessage()).toEqual({
|
||||||
|
title: '陶泥儿',
|
||||||
|
path: '/pages/web-view/index',
|
||||||
|
});
|
||||||
|
expect(page.onShareTimeline()).toEqual({
|
||||||
|
title: '陶泥儿',
|
||||||
|
query: '',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('默认匿名进入 web-view 仍不依赖 API_BASE_URL 配置', async () => {
|
test('默认匿名进入 web-view 仍不依赖 API_BASE_URL 配置', async () => {
|
||||||
@@ -140,6 +187,51 @@ describe('mini-program web-view auth page', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('体验版自动切到 dev 子域名并透传 trial 环境', async () => {
|
||||||
|
const wxMock = createWxMock();
|
||||||
|
wxMock.getAccountInfoSync.mockReturnValue({
|
||||||
|
miniProgram: { envVersion: 'trial' },
|
||||||
|
});
|
||||||
|
const page = loadWebViewPage(wxMock);
|
||||||
|
|
||||||
|
await page.onLoad({});
|
||||||
|
|
||||||
|
expect(page.data.webViewUrl).toBe(
|
||||||
|
'https://dev.genarrative.world/?clientType=mini_program&clientRuntime=wechat_mini_program&miniProgramEnv=trial',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('开发版自动切到 dev 子域名并把 develop 规整为 dev', async () => {
|
||||||
|
const wxMock = createWxMock();
|
||||||
|
wxMock.getAccountInfoSync.mockReturnValue({
|
||||||
|
miniProgram: { envVersion: 'develop' },
|
||||||
|
});
|
||||||
|
wxMock.login.mockImplementation(({ success }) => {
|
||||||
|
success({ code: 'wx-login-code' });
|
||||||
|
});
|
||||||
|
wxMock.request.mockImplementation(({ success }) => {
|
||||||
|
success({
|
||||||
|
statusCode: 200,
|
||||||
|
data: {
|
||||||
|
token: 'jwt-pending-wechat',
|
||||||
|
bindingStatus: 'pending_bind_phone',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const page = loadWebViewPage(wxMock);
|
||||||
|
|
||||||
|
await page.onLoad({ authAction: 'login', returnTo: 'previous' });
|
||||||
|
|
||||||
|
expect(wxMock.request).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
url: 'https://dev.genarrative.world/api/auth/wechat/miniprogram-login',
|
||||||
|
header: expect.objectContaining({
|
||||||
|
'x-mini-program-env': 'dev',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('onShow 二次检查支付结果并写回 web-view hash', () => {
|
test('onShow 二次检查支付结果并写回 web-view hash', () => {
|
||||||
const wxMock = createWxMock();
|
const wxMock = createWxMock();
|
||||||
wxMock.getStorageSync.mockImplementation((key) =>
|
wxMock.getStorageSync.mockImplementation((key) =>
|
||||||
|
|||||||
Reference in New Issue
Block a user