Sfoglia il codice sorgente

refactor(is): remove check for in, add tests (#927)

* refactor(is): remove check for in, add tests

* refactor(is): add type predicates
andretchen0 3 mesi fa
parent
commit
3e17e6805d
2 ha cambiato i file con 128 aggiunte e 287 eliminazioni
  1. 119 278
      src/utils/is.test.ts
  2. 9 9
      src/utils/is.ts

+ 119 - 278
src/utils/is.test.ts

@@ -1,285 +1,126 @@
-import { BufferGeometry, Fog, MeshBasicMaterial, MeshNormalMaterial, Object3D, PerspectiveCamera } from 'three'
+import type { Camera, Light, Material, Object3D } from 'three'
+import { AmbientLight, BufferGeometry, DirectionalLight, Fog, Group, Mesh, MeshBasicMaterial, MeshNormalMaterial, OrthographicCamera, PerspectiveCamera, PointLight, Scene } from 'three'
 import * as is from './is'
 
-describe('is', () => {
-  describe('is.num(a: any)', () => {
-    describe('true', () => {
-      it('number', () => {
-        assert(is.num(0))
-        assert(is.num(-1))
-        assert(is.num(Math.PI))
-        assert(is.num(Number.POSITIVE_INFINITY))
-        assert(is.num(Number.NEGATIVE_INFINITY))
-        assert(is.num(42))
-        assert(is.num(0b1111))
-        assert(is.num(0o17))
-        assert(is.num(0xF))
-      })
-    })
-    describe('false', () => {
-      it('null', () => {
-        assert(!is.num(null))
-      })
-      it('undefined', () => {
-        assert(!is.num(undefined))
-      })
-      it('string', () => {
-        assert(!is.num(''))
-        assert(!is.num('1'))
-      })
-      it('function', () => {
-        assert(!is.num(() => {}))
-        assert(!is.num(() => 1))
-      })
-      it('array', () => {
-        assert(!is.num([]))
-        assert(!is.num([1]))
-      })
-    })
-  })
-  describe('is.und(a: any)', () => {
-    describe('true', () => {
-      it('undefined', () => {
-        assert(is.und(undefined))
-      })
-    })
-    describe('false', () => {
-      it('null', () => {
-        assert(!is.und(null))
-      })
-      it('number', () => {
-        assert(!is.und(0))
-        assert(!is.und(-1))
-        assert(!is.und(Math.PI))
-        assert(!is.und(Number.POSITIVE_INFINITY))
-        assert(!is.und(Number.NEGATIVE_INFINITY))
-        assert(!is.und(42))
-      })
-      it('string', () => {
-        assert(!is.und(''))
-        assert(!is.und('tresObject'))
-      })
-      it('function', () => {
-        assert(!is.und(() => {}))
-      })
-      it('array', () => {
-        assert(!is.und([]))
-      })
-    })
-  })
-  describe('is.tresObject(a: any)', () => {
-    describe('true', () => {
-      it('object3D', () => {
-        assert(is.tresObject(new Object3D()))
-      })
-      it('bufferGeometry', () => {
-        assert(is.tresObject(new BufferGeometry()))
-      })
-      it('material', () => {
-        assert(is.tresObject(new MeshNormalMaterial()))
-      })
-      it('fog', () => {
-        assert(is.tresObject(new Fog('red')))
-      })
-      it('camera', () => {
-        assert(is.tresObject(new PerspectiveCamera()))
-      })
-    })
-    describe('false', () => {
-      it('undefined', () => {
-        assert(!is.tresObject(undefined))
-      })
-      it('null', () => {
-        assert(!is.tresObject(null))
-      })
-      it('number', () => {
-        assert(!is.tresObject(0))
-        assert(!is.tresObject(Math.PI))
-        assert(!is.tresObject(Number.POSITIVE_INFINITY))
-        assert(!is.tresObject(Number.NEGATIVE_INFINITY))
-        assert(!is.tresObject(42))
-      })
-      it('string', () => {
-        assert(!is.tresObject(''))
-        assert(!is.tresObject('tresObject'))
-      })
-      it('function', () => {
-        assert(!is.tresObject(() => {}))
-        assert(!is.tresObject(() => {}))
-      })
-    })
-  })
+const NUMBERS: Record<string, number> = {
+  '0': 0,
+  '1': 1,
+  '-1': -1,
+  '42': 42,
+  'Math.PI': Math.PI,
+  '-inf': Number.NEGATIVE_INFINITY,
+  '+inf': Number.POSITIVE_INFINITY,
+  '0b1111': 0b1111,
+  '0o17': 0o17,
+  '0xF': 0xF,
+}
 
-  describe('is.bufferGeometry(a: any)', () => {
-    describe('true', () => {
-      it('bufferGeometry', () => {
-        assert(is.bufferGeometry(new BufferGeometry()))
-      })
-    })
-    describe('false', () => {
-      it('object3D', () => {
-        assert(!is.bufferGeometry(new Object3D()))
-      })
-      it('material', () => {
-        assert(!is.bufferGeometry(new MeshNormalMaterial()))
-      })
-      it('fog', () => {
-        assert(!is.bufferGeometry(new Fog('red')))
-      })
-      it('camera', () => {
-        assert(!is.bufferGeometry(new PerspectiveCamera()))
-      })
-      it('undefined', () => {
-        assert(!is.bufferGeometry(undefined))
-      })
-      it('null', () => {
-        assert(!is.bufferGeometry(null))
-      })
-      it('number', () => {
-        assert(!is.bufferGeometry(0))
-        assert(!is.bufferGeometry(Math.PI))
-        assert(!is.bufferGeometry(Number.POSITIVE_INFINITY))
-        assert(!is.bufferGeometry(Number.NEGATIVE_INFINITY))
-        assert(!is.bufferGeometry(42))
-      })
-      it('string', () => {
-        assert(!is.bufferGeometry(''))
-        assert(!is.bufferGeometry('bufferGeometry'))
-      })
-      it('function', () => {
-        assert(!is.bufferGeometry(() => {}))
-        assert(!is.bufferGeometry(() => {}))
-      })
-    })
-  })
+const BOOLEANS: Record<string, boolean> = {
+  true: true,
+  false: false,
+}
 
-  describe('is.material(a: any)', () => {
-    describe('true', () => {
-      it('material', () => {
-        assert(is.material(new MeshNormalMaterial()))
-        assert(is.material(new MeshBasicMaterial()))
-      })
-    })
-    describe('false', () => {
-      it('object3D', () => {
-        assert(!is.bufferGeometry(new Object3D()))
-      })
-      it('bufferGeometry', () => {
-        assert(!is.bufferGeometry(new MeshNormalMaterial()))
-      })
-      it('fog', () => {
-        assert(!is.bufferGeometry(new Fog('red')))
-      })
-      it('camera', () => {
-        assert(!is.bufferGeometry(new PerspectiveCamera()))
-      })
-      it('undefined', () => {
-        assert(!is.bufferGeometry(undefined))
-      })
-      it('null', () => {
-        assert(!is.bufferGeometry(null))
-      })
-      it('number', () => {
-        assert(!is.bufferGeometry(0))
-        assert(!is.bufferGeometry(Math.PI))
-        assert(!is.bufferGeometry(Number.POSITIVE_INFINITY))
-        assert(!is.bufferGeometry(Number.NEGATIVE_INFINITY))
-        assert(!is.bufferGeometry(42))
-      })
-      it('string', () => {
-        assert(!is.bufferGeometry(''))
-        assert(!is.bufferGeometry('bufferGeometry'))
-      })
-      it('function', () => {
-        assert(!is.bufferGeometry(() => {}))
-        assert(!is.bufferGeometry(() => {}))
-      })
-    })
-  })
+const STRINGS: Record<string, string> = {
+  a: 'a',
+  aa: 'aa',
+  stringEmpty: '',
+  stringNumber: '0',
+}
 
-  describe('is.camera(a: any)', () => {
-    describe('true', () => {
-      it('camera', () => {
-        assert(is.camera(new PerspectiveCamera()))
-      })
-    })
-    describe('false', () => {
-      it('object3D', () => {
-        assert(!is.camera(new Object3D()))
-      })
-      it('bufferGeometry', () => {
-        assert(!is.camera(new BufferGeometry()))
-      })
-      it('material', () => {
-        assert(!is.camera(new MeshNormalMaterial()))
-      })
-      it('fog', () => {
-        assert(!is.camera(new Fog('red')))
-      })
-      it('undefined', () => {
-        assert(!is.camera(undefined))
-      })
-      it('null', () => {
-        assert(!is.camera(null))
-      })
-      it('number', () => {
-        assert(!is.camera(0))
-        assert(!is.camera(Math.PI))
-        assert(!is.camera(Number.POSITIVE_INFINITY))
-        assert(!is.camera(Number.NEGATIVE_INFINITY))
-        assert(!is.camera(42))
-      })
-      it('string', () => {
-        assert(!is.camera(''))
-        assert(!is.camera('camera'))
-      })
-      it('function', () => {
-        assert(!is.camera(() => {}))
-        assert(!is.camera(() => {}))
-      })
-    })
-  })
+const NULL: Record<string, null> = {
+  null: null,
+}
 
-  describe('is.fog(a: any)', () => {
-    describe('true', () => {
-      it('fog', () => {
-        assert(is.fog(new Fog('red')))
-      })
-    })
-    describe('false', () => {
-      it('object3D', () => {
-        assert(!is.fog(new Object3D()))
-      })
-      it('camera', () => {
-        assert(!is.fog(new PerspectiveCamera()))
-      })
-      it('bufferGeometry', () => {
-        assert(!is.fog(new BufferGeometry()))
-      })
-      it('material', () => {
-        assert(!is.fog(new MeshNormalMaterial()))
-      })
-      it('undefined', () => {
-        assert(!is.fog(undefined))
-      })
-      it('null', () => {
-        assert(!is.fog(null))
-      })
-      it('number', () => {
-        assert(!is.fog(0))
-        assert(!is.fog(Math.PI))
-        assert(!is.fog(Number.POSITIVE_INFINITY))
-        assert(!is.fog(Number.NEGATIVE_INFINITY))
-        assert(!is.fog(42))
-      })
-      it('string', () => {
-        assert(!is.fog(''))
-        assert(!is.fog('camera'))
-      })
-      it('function', () => {
-        assert(!is.fog(() => {}))
-        assert(!is.fog(() => {}))
-      })
-    })
-  })
+const UNDEFINED: Record<string, undefined> = {
+  undefined,
+}
+
+const ARRAYS: Record<string, any[]> = {
+  arrayEmpty: [],
+  arrayZero: [0],
+  arrayString: ['a'],
+  arrayMixed: ['', 0, undefined],
+}
+
+const FUNCTIONS: Record<string, (...a: any) => any> = {
+  functionVoid: () => {},
+  functionUndefined: () => undefined,
+  functionNull: () => null,
+  functionString: () => 'a',
+  functionZero: () => 0,
+}
+
+const FOGS: Record<string, Fog> = {
+  fogRed: new Fog('red'),
+  fogBlue: new Fog('blue'),
+}
+
+const MATERIALS: Record<string, Material> = {
+  meshNormalMaterial: new MeshNormalMaterial(),
+  meshBasicMaterial: new MeshBasicMaterial(),
+}
+
+const CAMERAS: Record<string, Camera> = {
+  perspectiveCamera: new PerspectiveCamera(),
+  orthographicCamera: new OrthographicCamera(),
+}
+
+const SCENES: Record<string, Scene> = {
+  scene: new Scene(),
+}
+
+const LIGHTS: Record<string, Light> = {
+  pointLight: new PointLight(),
+  directionalLight: new DirectionalLight(),
+  ambientLight: new AmbientLight(),
+}
+
+const OBJECT3DS: Record<string, Object3D> = {
+  mesh: new Mesh(),
+  group: new Group(),
+  ...SCENES,
+  ...LIGHTS,
+  ...CAMERAS,
+}
+
+const BUFFER_GEOMETRIES: Record<string, BufferGeometry> = {
+  bufferGeometry: new BufferGeometry(),
+}
+
+const OBJECTS = Object.assign({}, { '{}': {}, '{ a: "a" }': { a: 'a' } }, FOGS, MATERIALS, OBJECT3DS, BUFFER_GEOMETRIES)
+
+// NOTE: See definition in is.ts
+const TRES_OBJECTS = Object.assign({}, MATERIALS, OBJECT3DS, BUFFER_GEOMETRIES, FOGS)
+
+const ALL = Object.assign({}, NUMBERS, BOOLEANS, STRINGS, NULL, UNDEFINED, ARRAYS, FUNCTIONS, OBJECTS)
+
+describe('is', () => {
+  describe('is.und(a: any)', () => { test(is.und, UNDEFINED) })
+  describe('is.arr(a: any)', () => { test(is.arr, ARRAYS) })
+  describe('is.num(a: any)', () => { test(is.num, NUMBERS) })
+  describe('is.str(a: any)', () => { test(is.str, STRINGS) })
+  describe('is.bool(a: any)', () => { test(is.bool, BOOLEANS) })
+  describe('is.fun(a: any)', () => { test(is.fun, FUNCTIONS) })
+  describe('is.obj(a: any)', () => { test(is.obj, OBJECTS) })
+  describe('is.object3D(a: any)', () => { test(is.object3D, OBJECT3DS) })
+  describe('is.camera(a: any)', () => { test(is.camera, CAMERAS) })
+  describe('is.bufferGeometry(a: any)', () => { test(is.bufferGeometry, BUFFER_GEOMETRIES) })
+  describe('is.material(a: any)', () => { test(is.material, MATERIALS) })
+  describe('is.light(a: any)', () => { test(is.light, LIGHTS) })
+  describe('is.fog(a: any)', () => { test(is.fog, FOGS) })
+  describe('is.scene(a: any)', () => { test(is.scene, SCENES) })
+  describe('is.tresObject(a: any)', () => { test(is.tresObject, TRES_OBJECTS) })
 })
+
+/**
+ * Test all values in `ALL`.
+ * `fn` should return `true` for all values in `truth` else `false`.
+ */
+function test(fn: (a: any) => boolean, truth: Record<string, any>) {
+  const trueKeys = new Set(Object.keys(truth))
+  for (const [key, value] of Object.entries(ALL)) {
+    it(`returns ${trueKeys.has(key)} for ${key}`, () => {
+      expect(fn(value)).toBe(trueKeys.has(key))
+    })
+  }
+}

+ 9 - 9
src/utils/is.ts

@@ -1,11 +1,11 @@
 import type { TresObject, TresPrimitive } from 'src/types'
 import type { BufferGeometry, Camera, Fog, Light, Material, Object3D, Scene } from 'three'
 
-export function und(u: unknown) {
+export function und(u: unknown): u is undefined {
   return typeof u === 'undefined'
 }
 
-export function arr(u: unknown) {
+export function arr(u: unknown): u is Array<unknown> {
   return Array.isArray(u)
 }
 
@@ -30,31 +30,31 @@ export function obj(u: unknown): u is Record<string | number | symbol, unknown>
 }
 
 export function object3D(u: unknown): u is Object3D {
-  return obj(u) && ('isObject3D' in u) && !!(u.isObject3D)
+  return obj(u) && !!(u.isObject3D)
 }
 
 export function camera(u: unknown): u is Camera {
-  return obj(u) && 'isCamera' in u && !!(u.isCamera)
+  return obj(u) && !!(u.isCamera)
 }
 
 export function bufferGeometry(u: unknown): u is BufferGeometry {
-  return obj(u) && 'isBufferGeometry' in u && !!(u.isBufferGeometry)
+  return obj(u) && !!(u.isBufferGeometry)
 }
 
 export function material(u: unknown): u is Material {
-  return obj(u) && 'isMaterial' in u && !!(u.isMaterial)
+  return obj(u) && !!(u.isMaterial)
 }
 
 export function light(u: unknown): u is Light {
-  return obj(u) && 'isLight' in u && !!(u.isLight)
+  return obj(u) && !!(u.isLight)
 }
 
 export function fog(u: unknown): u is Fog {
-  return obj(u) && 'isFog' in u && !!(u.isFog)
+  return obj(u) && !!(u.isFog)
 }
 
 export function scene(u: unknown): u is Scene {
-  return obj(u) && 'isScene' in u && !!(u.isScene)
+  return obj(u) && !!(u.isScene)
 }
 
 export function tresObject(u: unknown): u is TresObject {