🐛 isPropertyReadonly support accessor assert (#2284)
This commit is contained in:
parent
0aaf55a77f
commit
9a343b4074
|
|
@ -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();
|
||||||
|
});
|
||||||
|
|
|
||||||
15
src/utils.ts
15
src/utils.ts
|
|
@ -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>();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user