app scaffold
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
// core_router 的高价值断言:
|
||||
// 1. 未选店时**绝不能**放行到业务页——那边 currentStoreIdProvider 会直接 throw。
|
||||
// 2. 会话未就绪时停在启动页,不能提前放行。
|
||||
// 3. H5 路由里只有 target,永远不出现裸 URL(安全)。
|
||||
|
||||
import 'package:core_auth/core_auth.dart';
|
||||
import 'package:core_router/core_router.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
const UserContext _user = UserContext(
|
||||
userId: 'U1',
|
||||
employeeId: 'E1',
|
||||
phone: '13800000000',
|
||||
roleCode: 'CLERK',
|
||||
channel: 'APP',
|
||||
);
|
||||
|
||||
const StoreContext _store = StoreContext(
|
||||
storeId: 7,
|
||||
storeCode: 'S007',
|
||||
storeName: '测试门店',
|
||||
orgId: 1,
|
||||
);
|
||||
|
||||
void main() {
|
||||
group('resolveRedirect', () {
|
||||
test('会话未就绪时停在启动页,不提前放行到业务页', () {
|
||||
expect(resolveRedirect(null, '/purchase/orders', Uri.parse('/purchase/orders')), '/splash');
|
||||
expect(resolveRedirect(null, '/splash', Uri.parse('/splash')), isNull);
|
||||
expect(resolveRedirect(const SessionLoading(), '/home', Uri.parse('/home')), '/splash');
|
||||
});
|
||||
|
||||
test('未登录访问业务页 → 登录页并带上回跳目标', () {
|
||||
final String? to = resolveRedirect(
|
||||
const SessionUnauthenticated(),
|
||||
'/purchase/orders',
|
||||
Uri.parse('/purchase/orders?id=9'),
|
||||
);
|
||||
expect(to, startsWith('/login?from='));
|
||||
expect(Uri.decodeComponent(Uri.parse(to!).queryParameters['from']!), '/purchase/orders?id=9');
|
||||
});
|
||||
|
||||
test('未登录停在登录页时不再跳转(否则死循环)', () {
|
||||
expect(
|
||||
resolveRedirect(const SessionUnauthenticated(), '/login', Uri.parse('/login')),
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
|
||||
test('已登录但未选店时,任何业务页都被挡回选店页', () {
|
||||
const AppSession awaiting = SessionAwaitingStore(user: _user, candidates: <StoreContext>[]);
|
||||
// 这条是核心:放行过去 currentStoreIdProvider 会 throw(11)。
|
||||
expect(resolveRedirect(awaiting, '/home', Uri.parse('/home')), '/store-picker');
|
||||
expect(resolveRedirect(awaiting, '/store-picker', Uri.parse('/store-picker')), isNull);
|
||||
});
|
||||
|
||||
group('已登录且已选店', () {
|
||||
const AppSession active = SessionActive(user: _user, store: _store);
|
||||
|
||||
test('停在启动页/选店页时进工作台', () {
|
||||
expect(resolveRedirect(active, '/splash', Uri.parse('/splash')), '/home');
|
||||
expect(resolveRedirect(active, '/store-picker', Uri.parse('/store-picker')), '/home');
|
||||
});
|
||||
|
||||
test('登录页带 from 时回跳原目标,没有 from 时进工作台', () {
|
||||
expect(
|
||||
resolveRedirect(
|
||||
active,
|
||||
'/login',
|
||||
Uri.parse('/login?from=${Uri.encodeComponent('/purchase/orders?id=9')}'),
|
||||
),
|
||||
'/purchase/orders?id=9',
|
||||
);
|
||||
expect(resolveRedirect(active, '/login', Uri.parse('/login')), '/home');
|
||||
});
|
||||
|
||||
test('业务页放行', () {
|
||||
expect(resolveRedirect(active, '/purchase/orders', Uri.parse('/purchase/orders')), isNull);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('菜单与 H5 路由', () {
|
||||
test('未知菜单 code 返回 null,由调用方隐藏并上报', () {
|
||||
expect(resolveMenuRoute('PURCHASE_ORDER'), '/purchase/orders');
|
||||
expect(resolveMenuRoute('SOMETHING_NEW_FROM_BACKEND'), isNull);
|
||||
});
|
||||
|
||||
test('H5 路由只带 target,不出现裸 URL', () {
|
||||
final String route = AppRoutes.webviewFor('QUOTE_ORDER', title: '报价开单');
|
||||
expect(route, startsWith('/webview?target=QUOTE_ORDER'));
|
||||
expect(route, isNot(contains('http')));
|
||||
expect(Uri.parse(route).queryParameters['title'], '报价开单');
|
||||
});
|
||||
|
||||
test('菜单表里的 H5 项也是 target 形态', () {
|
||||
for (final String path in menuRouteMap.values) {
|
||||
expect(path, isNot(contains('http')), reason: '路由里塞裸 URL 是明确的安全洞');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user