🐛 isPropertyReadonly support accessor assert (#2284)

This commit is contained in:
Kuitos 2022-09-26 20:57:39 +08:00 committed by GitHub
parent 0aaf55a77f
commit 9a343b4074
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 47 additions and 16 deletions

View File

@ -5,24 +5,25 @@ import {
getDefaultTplWrapper, getDefaultTplWrapper,
getWrapperId, getWrapperId,
getXPathForElement, getXPathForElement,
isPropertyReadonly,
nextTask, nextTask,
sleep, sleep,
validateExportLifecycle, validateExportLifecycle,
} from '../utils'; } from '../utils';
test('should wrap the id [1]', () => { it('should wrap the id [1]', () => {
const id = 'REACT16'; const id = 'REACT16';
expect(getWrapperId(id)).toBe(`__qiankun_microapp_wrapper_for_${'react_16'}__`); expect(getWrapperId(id)).toBe(`__qiankun_microapp_wrapper_for_${'react_16'}__`);
}); });
test('should wrap the id [2]', () => { it('should wrap the id [2]', () => {
const id = 'react16'; const id = 'react16';
expect(getWrapperId(id)).toBe('__qiankun_microapp_wrapper_for_react_16__'); expect(getWrapperId(id)).toBe('__qiankun_microapp_wrapper_for_react_16__');
}); });
test('should wrap string with div', () => { it('should wrap string with div', () => {
const tpl = '<span>qiankun</span>'; const tpl = '<span>qiankun</span>';
const factory = getDefaultTplWrapper('react16'); const factory = getDefaultTplWrapper('react16');
@ -34,7 +35,7 @@ test('should wrap string with div', () => {
); );
}); });
test('should be able to validate lifecycle', () => { it('should be able to validate lifecycle', () => {
const noop = () => undefined; const noop = () => undefined;
const export1 = { const export1 = {
@ -71,7 +72,7 @@ test.skip('should be able to suspend', async () => {
expect(diff >= 10).toBeTruthy(); expect(diff >= 10).toBeTruthy();
}); });
test('Deferred should worked [1]', async () => { it('Deferred should worked [1]', async () => {
const inst = new Deferred(); const inst = new Deferred();
setTimeout(() => { setTimeout(() => {
@ -82,7 +83,7 @@ test('Deferred should worked [1]', async () => {
expect(ret).toBe(1); expect(ret).toBe(1);
}); });
test('Deferred should worked [2]', async () => { it('Deferred should worked [2]', async () => {
const inst = new Deferred(); const inst = new Deferred();
setTimeout(() => { setTimeout(() => {
@ -100,7 +101,7 @@ test('Deferred should worked [2]', async () => {
expect(err).toBeInstanceOf(Error); expect(err).toBeInstanceOf(Error);
}); });
test('should getXPathForElement work well', () => { it('should getXPathForElement work well', () => {
const article = document.createElement('article'); const article = document.createElement('article');
article.innerHTML = ` article.innerHTML = `
<div> <div>
@ -140,7 +141,7 @@ it('should nextTick just executed once in one task context', async () => {
expect(counter).toBe(3); expect(counter).toBe(3);
}); });
it('should genAppInstanceIdByName works well', () => { it('should genAppInstanceIdByName work well', () => {
const instanceId1 = genAppInstanceIdByName('hello'); const instanceId1 = genAppInstanceIdByName('hello');
expect(instanceId1).toBe('hello'); expect(instanceId1).toBe('hello');
@ -150,3 +151,34 @@ it('should genAppInstanceIdByName works well', () => {
const instanceId3 = genAppInstanceIdByName('hello'); const instanceId3 = genAppInstanceIdByName('hello');
expect(instanceId3).toBe('hello_2'); expect(instanceId3).toBe('hello_2');
}); });
it('should isPropertyReadonly work well', () => {
const a = {
get name() {
return 'read only';
},
};
expect(isPropertyReadonly(a, 'name')).toBeTruthy();
const b = {
get name() {
return 'read only';
},
set name(_) {},
};
expect(isPropertyReadonly(b, 'name')).toBeFalsy();
const c = {};
Object.defineProperty(c, 'name', { writable: false });
expect(isPropertyReadonly(c, 'name')).toBeTruthy();
const d = {};
Object.defineProperty(d, 'name', { configurable: true });
expect(isPropertyReadonly(d, 'name')).toBeTruthy();
const e = {};
Object.defineProperty(e, 'name', { writable: true });
expect(isPropertyReadonly(e, 'name')).toBeFalsy();
expect(isPropertyReadonly(undefined, 'name')).toBeFalsy();
});

View File

@ -96,27 +96,26 @@ export const isCallable = (fn: any) => {
*/ */
const propertyReadonlyCacheMap = new WeakMap<any, Record<PropertyKey, boolean>>(); const propertyReadonlyCacheMap = new WeakMap<any, Record<PropertyKey, boolean>>();
export function isPropertyReadonly(target: any, p?: PropertyKey): boolean { export function isPropertyReadonly(target: any, p?: PropertyKey): boolean {
// 异常返回
if (!target || !p) { if (!target || !p) {
return false; return false;
} }
// 取缓存
const targetPropertiesFromCache = propertyReadonlyCacheMap.get(target) || {}; const targetPropertiesFromCache = propertyReadonlyCacheMap.get(target) || {};
if (typeof targetPropertiesFromCache[p] === 'boolean') { if (targetPropertiesFromCache[p]) {
return targetPropertiesFromCache[p]; return targetPropertiesFromCache[p];
} }
// 计算
const propertyDescriptor = Object.getOwnPropertyDescriptor(target, p); const propertyDescriptor = Object.getOwnPropertyDescriptor(target, p);
const result = propertyDescriptor?.configurable === false && propertyDescriptor?.writable === false; const readonly = Boolean(
propertyDescriptor &&
(propertyDescriptor.writable === false || (propertyDescriptor.get && !propertyDescriptor.set)),
);
// 写缓存 targetPropertiesFromCache[p] = readonly;
targetPropertiesFromCache[p] = result;
propertyReadonlyCacheMap.set(target, targetPropertiesFromCache); propertyReadonlyCacheMap.set(target, targetPropertiesFromCache);
return result; return readonly;
} }
const boundedMap = new WeakMap<CallableFunction, boolean>(); const boundedMap = new WeakMap<CallableFunction, boolean>();