diff --git a/.changeset/fresh-computed-super.md b/.changeset/fresh-computed-super.md deleted file mode 100644 index ac32aa9d3c..0000000000 --- a/.changeset/fresh-computed-super.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"mobx": patch ---- - -Fix Stage 3 `@computed` overrides that delegate to a same-named parent getter via `super`. diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 9fc4221281..38bd174f6a 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -38,8 +38,5 @@ jobs: - name: Test size run: npm run test:size - - name: Test flow - run: npm -w mobx run test:flow - - name: Test performance run: npm -w mobx run test:performance diff --git a/package-lock.json b/package-lock.json index 0a15d499ec..812d355475 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11557,19 +11557,6 @@ "dev": true, "license": "MIT" }, - "node_modules/flow-bin": { - "version": "0.123.0", - "resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.123.0.tgz", - "integrity": "sha512-Ylcf8YDIM/KrqtxkPuq+f8O+6sdYA2Nuz5f+sWHlp539DatZz3YMcsO1EiXaf1C11HJgpT/3YGYe7xZ9/UZmvQ==", - "dev": true, - "license": "MIT", - "bin": { - "flow": "cli.js" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/flow-parser": { "version": "0.237.2", "resolved": "https://registry.npmjs.org/flow-parser/-/flow-parser-0.237.2.tgz", @@ -25124,7 +25111,7 @@ } }, "packages/mobx": { - "version": "6.15.3", + "version": "6.16.0", "license": "MIT", "devDependencies": { "@babel/core": "^7.9.0", @@ -25134,8 +25121,7 @@ "@babel/preset-env": "^7.9.5", "@babel/preset-typescript": "^7.9.0", "@babel/runtime": "^7.9.2", - "conditional-type-checks": "^1.0.5", - "flow-bin": "^0.123.0" + "conditional-type-checks": "^1.0.5" }, "funding": { "type": "opencollective", @@ -25143,7 +25129,7 @@ } }, "packages/mobx-react": { - "version": "9.2.1", + "version": "9.2.2", "license": "MIT", "dependencies": { "mobx-react-lite": "^4.1.1" @@ -25198,7 +25184,7 @@ } }, "packages/mobx-undecorate": { - "version": "1.3.0", + "version": "1.3.1", "license": "MIT", "dependencies": { "@babel/core": "^7.9.0", diff --git a/packages/mobx/.flowconfig b/packages/mobx/.flowconfig deleted file mode 100644 index 066ae6aa76..0000000000 --- a/packages/mobx/.flowconfig +++ /dev/null @@ -1,11 +0,0 @@ -[ignore] -.*/node_modules - -[include] -__tests__/v5/flow - -[libs] -flow-typed/ - -[options] -suppress_comment= \\(.\\|\n\\)*\\$ExpectError diff --git a/packages/mobx/CHANGELOG.md b/packages/mobx/CHANGELOG.md index ee9e613437..102c06c0ff 100644 --- a/packages/mobx/CHANGELOG.md +++ b/packages/mobx/CHANGELOG.md @@ -1,5 +1,13 @@ # mobx +## 6.16.1 + +### Patch Changes + +- [`7fd93348e81f484c4ec2dcca8cde5fa6ed7412fe`](https://github.com/mobxjs/mobx/commit/7fd93348e81f484c4ec2dcca8cde5fa6ed7412fe) [#4661](https://github.com/mobxjs/mobx/pull/4661) Thanks [@js2me](https://github.com/js2me)! - Fix Stage 3 `@computed` overrides that delegate to a same-named parent getter via `super`. + +- [`02afa5b6c33441737df81d344161a03bb9aea0c8`](https://github.com/mobxjs/mobx/commit/02afa5b6c33441737df81d344161a03bb9aea0c8) [#4666](https://github.com/mobxjs/mobx/pull/4666) Thanks [@kubk](https://github.com/kubk)! - Remove outdated Flow typechecker support artifacts from the package. + ## 6.16.0 ### Minor Changes diff --git a/packages/mobx/__tests__/.babelrc b/packages/mobx/__tests__/.babelrc deleted file mode 100644 index 92f82e30cb..0000000000 --- a/packages/mobx/__tests__/.babelrc +++ /dev/null @@ -1,13 +0,0 @@ -{ - "presets": ["@babel/preset-env"], - "plugins": [ - [ - "@babel/plugin-transform-runtime", - { - "regenerator": true - } - ], - ["@babel/plugin-proposal-decorators", { "legacy": true }], - ["@babel/plugin-proposal-class-properties", { "loose": false }] - ] -} diff --git a/packages/mobx/__tests__/v5/base/__snapshots__/action.js.snap b/packages/mobx/__tests__/base/__snapshots__/action.js.snap similarity index 100% rename from packages/mobx/__tests__/v5/base/__snapshots__/action.js.snap rename to packages/mobx/__tests__/base/__snapshots__/action.js.snap diff --git a/packages/mobx/__tests__/v5/base/__snapshots__/extras.js.snap b/packages/mobx/__tests__/base/__snapshots__/extras.js.snap similarity index 100% rename from packages/mobx/__tests__/v5/base/__snapshots__/extras.js.snap rename to packages/mobx/__tests__/base/__snapshots__/extras.js.snap diff --git a/packages/mobx/__tests__/v5/base/__snapshots__/flow.js.snap b/packages/mobx/__tests__/base/__snapshots__/flow.js.snap similarity index 100% rename from packages/mobx/__tests__/v5/base/__snapshots__/flow.js.snap rename to packages/mobx/__tests__/base/__snapshots__/flow.js.snap diff --git a/packages/mobx/__tests__/v4/base/__snapshots__/makereactive.js.snap b/packages/mobx/__tests__/base/__snapshots__/makereactive.js.snap similarity index 100% rename from packages/mobx/__tests__/v4/base/__snapshots__/makereactive.js.snap rename to packages/mobx/__tests__/base/__snapshots__/makereactive.js.snap diff --git a/packages/mobx/__tests__/v5/base/__snapshots__/object-api.js.snap b/packages/mobx/__tests__/base/__snapshots__/object-api.js.snap similarity index 100% rename from packages/mobx/__tests__/v5/base/__snapshots__/object-api.js.snap rename to packages/mobx/__tests__/base/__snapshots__/object-api.js.snap diff --git a/packages/mobx/__tests__/v4/base/__snapshots__/observables.js.snap b/packages/mobx/__tests__/base/__snapshots__/observables.js.snap similarity index 100% rename from packages/mobx/__tests__/v4/base/__snapshots__/observables.js.snap rename to packages/mobx/__tests__/base/__snapshots__/observables.js.snap diff --git a/packages/mobx/__tests__/v5/base/__snapshots__/proxies.js.snap b/packages/mobx/__tests__/base/__snapshots__/proxies.js.snap similarity index 100% rename from packages/mobx/__tests__/v5/base/__snapshots__/proxies.js.snap rename to packages/mobx/__tests__/base/__snapshots__/proxies.js.snap diff --git a/packages/mobx/__tests__/v5/base/__snapshots__/spy.js.snap b/packages/mobx/__tests__/base/__snapshots__/spy.js.snap similarity index 100% rename from packages/mobx/__tests__/v5/base/__snapshots__/spy.js.snap rename to packages/mobx/__tests__/base/__snapshots__/spy.js.snap diff --git a/packages/mobx/__tests__/v5/base/action.js b/packages/mobx/__tests__/base/action.js similarity index 99% rename from packages/mobx/__tests__/v5/base/action.js rename to packages/mobx/__tests__/base/action.js index 5702578252..782faa77eb 100644 --- a/packages/mobx/__tests__/v5/base/action.js +++ b/packages/mobx/__tests__/base/action.js @@ -1,4 +1,4 @@ -import * as mobx from "../../../src/mobx" +import * as mobx from "../../src/mobx" import * as utils from "../utils/test-utils" test("action should wrap in transaction", () => { @@ -341,7 +341,6 @@ test("runInAction", () => { { arguments: [], name: "" } ]) - mobx.configure({ enforceActions: "never" }) spyDisposer() d() diff --git a/packages/mobx/__tests__/v5/base/api.js b/packages/mobx/__tests__/base/api.js similarity index 95% rename from packages/mobx/__tests__/v5/base/api.js rename to packages/mobx/__tests__/base/api.js index 5dff66a63a..4f705e0806 100644 --- a/packages/mobx/__tests__/v5/base/api.js +++ b/packages/mobx/__tests__/base/api.js @@ -1,4 +1,4 @@ -const mobx = require("../../../src/mobx.ts") +const mobx = require("../../src/mobx.ts") test("correct api should be exposed", function () { expect( @@ -80,6 +80,6 @@ test("correct api should be exposed", function () { }) test("mobx has no dependencies", () => { - const pkg = require("../../../package.json") + const pkg = require("../../package.json") expect(pkg.dependencies).toEqual({}) }) diff --git a/packages/mobx/__tests__/v5/base/array.js b/packages/mobx/__tests__/base/array.js similarity index 99% rename from packages/mobx/__tests__/v5/base/array.js rename to packages/mobx/__tests__/base/array.js index 5b07513987..2fe2b85f49 100644 --- a/packages/mobx/__tests__/v5/base/array.js +++ b/packages/mobx/__tests__/base/array.js @@ -1,6 +1,6 @@ "use strict" -const mobx = require("../../../src/mobx.ts") +const mobx = require("../../src/mobx.ts") const { observable, when, _getAdministration, reaction, computed, makeObservable, autorun } = mobx const iterall = require("iterall") @@ -188,7 +188,7 @@ test("findLast(findLastIndex) and remove", function () { } return false } - ;[].findLastIndex; + ;[].findLastIndex expect(a.findLast(predicate)).toBe(20) expect(a.findLastIndex(predicate)).toBe(2) expect(a.findLast(predicate)).toBe(20) diff --git a/packages/mobx/__tests__/v5/base/autorun.js b/packages/mobx/__tests__/base/autorun.js similarity index 94% rename from packages/mobx/__tests__/v5/base/autorun.js rename to packages/mobx/__tests__/base/autorun.js index 72499f0dc3..a8ec83ca22 100644 --- a/packages/mobx/__tests__/v5/base/autorun.js +++ b/packages/mobx/__tests__/base/autorun.js @@ -1,8 +1,8 @@ /** - * @type {typeof import("./../../../src/v5/mobx")} + * @type {typeof import("./../../src/mobx")} */ -const mobx = require("../../../src/mobx.ts") -const utils = require("../../v5/utils/test-utils") +const mobx = require("../../src/mobx.ts") +const utils = require("../utils/test-utils") test("autorun passes Reaction as an argument to view function", function () { const a = mobx.observable.box(1) @@ -143,8 +143,6 @@ test("when effect is an action", function (done) { () => a.get() === 1, () => { a.set(2) - - mobx.configure({ enforceActions: "never" }) done() }, { timeout: 1 } diff --git a/packages/mobx/__tests__/v5/base/autorunAsync.js b/packages/mobx/__tests__/base/autorunAsync.js similarity index 98% rename from packages/mobx/__tests__/v5/base/autorunAsync.js rename to packages/mobx/__tests__/base/autorunAsync.js index 028a81c3a6..4fd5fe65c3 100644 --- a/packages/mobx/__tests__/v5/base/autorunAsync.js +++ b/packages/mobx/__tests__/base/autorunAsync.js @@ -1,9 +1,9 @@ /** - * @type {typeof import("../../../src/v5/mobx")} + * @type {typeof import("../../src/mobx")} */ -const mobx = require("../../../src/mobx.ts") +const mobx = require("../../src/mobx.ts") -const utils = require("../../v5/utils/test-utils") +const utils = require("../utils/test-utils") const { $mobx } = mobx diff --git a/packages/mobx/__tests__/v5/base/babel-decorators.js b/packages/mobx/__tests__/base/babel-decorators.js similarity index 99% rename from packages/mobx/__tests__/v5/base/babel-decorators.js rename to packages/mobx/__tests__/base/babel-decorators.js index 69e14ee6d6..c90214e9be 100644 --- a/packages/mobx/__tests__/v5/base/babel-decorators.js +++ b/packages/mobx/__tests__/base/babel-decorators.js @@ -14,8 +14,8 @@ import { isAction, configure, makeObservable -} from "../../../src/mobx" -import * as mobx from "../../../src/mobx" +} from "../../src/mobx" +import * as mobx from "../../src/mobx" test("babel", function () { class Box { @@ -428,8 +428,6 @@ test("267 (babel) should be possible to declare properties observable outside st } } Store // just to avoid linter warning - - configure({ enforceActions: "never" }) }) test("288 atom not detected for object property", () => { diff --git a/packages/mobx/__tests__/v5/base/babel-tests.js b/packages/mobx/__tests__/base/babel-tests.js similarity index 99% rename from packages/mobx/__tests__/v5/base/babel-tests.js rename to packages/mobx/__tests__/base/babel-tests.js index 3a85dcca98..8f7a5d3f99 100644 --- a/packages/mobx/__tests__/v5/base/babel-tests.js +++ b/packages/mobx/__tests__/base/babel-tests.js @@ -14,8 +14,8 @@ import { isAction, configure, makeObservable -} from "../../../src/mobx" -import * as mobx from "../../../src/mobx" +} from "../../src/mobx" +import * as mobx from "../../src/mobx" test("babel", function () { class Box { @@ -489,8 +489,6 @@ test("267 (babel) should be possible to declare properties observable outside st } } Store // just to avoid linter warning - - configure({ enforceActions: "never" }) }) test("288 atom not detected for object property", () => { diff --git a/packages/mobx/__tests__/v5/base/become-observed.ts b/packages/mobx/__tests__/base/become-observed.ts similarity index 99% rename from packages/mobx/__tests__/v5/base/become-observed.ts rename to packages/mobx/__tests__/base/become-observed.ts index a075422f4b..8222cdbd06 100644 --- a/packages/mobx/__tests__/v5/base/become-observed.ts +++ b/packages/mobx/__tests__/base/become-observed.ts @@ -8,7 +8,7 @@ import { onBecomeUnobserved, runInAction, makeAutoObservable -} from "../../../src/mobx" +} from "../../src/mobx" describe("become-observed", () => { it("work on map with number as key", () => { diff --git a/packages/mobx/__tests__/v4/base/cycles.js b/packages/mobx/__tests__/base/cycles.js similarity index 99% rename from packages/mobx/__tests__/v4/base/cycles.js rename to packages/mobx/__tests__/base/cycles.js index c35cfb124b..5b38f48600 100644 --- a/packages/mobx/__tests__/v4/base/cycles.js +++ b/packages/mobx/__tests__/base/cycles.js @@ -1,4 +1,4 @@ -const m = require("../mobx4") +const m = require("../../src/mobx.ts") test("cascading active state (form 1)", function () { const Store = function () { diff --git a/packages/mobx/__tests__/v5/base/decorate.js b/packages/mobx/__tests__/base/decorate.js similarity index 99% rename from packages/mobx/__tests__/v5/base/decorate.js rename to packages/mobx/__tests__/base/decorate.js index e1b44a0c2a..0197f11a03 100644 --- a/packages/mobx/__tests__/v5/base/decorate.js +++ b/packages/mobx/__tests__/base/decorate.js @@ -12,7 +12,7 @@ import { isComputedProp, isAction, makeObservable -} from "../../../src/mobx" +} from "../../src/mobx" // @ts-ignore import { primitive, serialize, deserialize, createModelSchema } from "serializr" diff --git a/packages/mobx/__tests__/v5/base/errorhandling.js b/packages/mobx/__tests__/base/errorhandling.js similarity index 95% rename from packages/mobx/__tests__/v5/base/errorhandling.js rename to packages/mobx/__tests__/base/errorhandling.js index 533c674083..0d5a8c377c 100644 --- a/packages/mobx/__tests__/v5/base/errorhandling.js +++ b/packages/mobx/__tests__/base/errorhandling.js @@ -1,6 +1,6 @@ -const mobx = require("../../../src/mobx.ts") +const mobx = require("../../src/mobx.ts") const m = mobx -const utils = require("../../v5/utils/test-utils") +const utils = require("../utils/test-utils") const { observable, computed, $mobx, autorun } = mobx @@ -739,42 +739,33 @@ test("it should be possible to handle global errors in reactions - 2 - #1480", ( test("global error handling will be skipped when using disableErrorBoundaries - 1", () => { utils.supressConsole(() => { mobx.configure({ disableErrorBoundaries: true }) - try { - mobx.observable.box(1) - - expect(() => { - mobx.autorun(function () { - throw "OOPS" - }) - }).toThrow(/OOPS/) - } finally { - mobx.configure({ disableErrorBoundaries: false }) - mobx._resetGlobalState() - } + + mobx.observable.box(1) + + expect(() => { + mobx.autorun(function () { + throw "OOPS" + }) + }).toThrow(/OOPS/) }) }) test("global error handling will be skipped when using disableErrorBoundaries - 2", () => { utils.supressConsole(() => { mobx.configure({ disableErrorBoundaries: true }) - try { - const a = mobx.observable.box(1) + const a = mobx.observable.box(1) - const d = mobx.reaction( - () => a.get(), - () => { - throw "OOPS" - } - ) - expect(() => { - a.set(2) - }).toThrow(/OOPS/) - - d() - } finally { - mobx.configure({ disableErrorBoundaries: false }) - mobx._resetGlobalState() - } + const d = mobx.reaction( + () => a.get(), + () => { + throw "OOPS" + } + ) + expect(() => { + a.set(2) + }).toThrow(/OOPS/) + + d() }) }) @@ -857,13 +848,6 @@ describe("es5 compat warnings", () => { `[Error: [MobX] MobX is currently configured to be able to run in ES5 mode, but in ES5 MobX won't be able to iterate keys to detect added / removed properties. Use 'keys' from 'mobx' instead.]` ) }) - - afterEach(() => { - mobx._resetGlobalState() - mobx.configure({ - useProxies: "always" - }) - }) }) test("should throw when adding properties in ES5 compat mode", () => {}) diff --git a/packages/mobx/__tests__/v5/base/extendObservable.js b/packages/mobx/__tests__/base/extendObservable.js similarity index 99% rename from packages/mobx/__tests__/v5/base/extendObservable.js rename to packages/mobx/__tests__/base/extendObservable.js index 7fdfd4b635..69977f5368 100644 --- a/packages/mobx/__tests__/v5/base/extendObservable.js +++ b/packages/mobx/__tests__/base/extendObservable.js @@ -10,7 +10,7 @@ import { extendObservable, observable, reaction -} from "../../../src/mobx" +} from "../../src/mobx" test("extendObservable should work", function () { class Box { diff --git a/packages/mobx/__tests__/v5/base/extras.js b/packages/mobx/__tests__/base/extras.js similarity index 99% rename from packages/mobx/__tests__/v5/base/extras.js rename to packages/mobx/__tests__/base/extras.js index 471b947abf..ec22d55a20 100644 --- a/packages/mobx/__tests__/v5/base/extras.js +++ b/packages/mobx/__tests__/base/extras.js @@ -1,4 +1,4 @@ -const mobx = require("../../../src/mobx.ts") +const mobx = require("../../src/mobx.ts") const m = mobx const { $mobx } = mobx @@ -914,7 +914,7 @@ test("Default debug names - development", () => { }) test("Default debug names - production", () => { - const mobx = require(`../../../dist/mobx.cjs.production.min.js`) + const mobx = require(`../../dist/mobx.cjs.production.min.js`) expect(mobx.getDebugName(mobx.observable({ x() {} }, { x: mobx.action }).x)).toBe("x") // perhaps should be ""?? expect(mobx.getDebugName(mobx.createAtom())).toBe("Atom") @@ -941,7 +941,7 @@ test("Default debug names - production", () => { test("User provided debug names are always respected", () => { const mobxDevelopment = mobx - const mobxProduction = require(`../../../dist/mobx.cjs.production.min.js`) + const mobxProduction = require(`../../dist/mobx.cjs.production.min.js`) const name = "CustomName" diff --git a/packages/mobx/__tests__/v5/base/flow.js b/packages/mobx/__tests__/base/flow.js similarity index 99% rename from packages/mobx/__tests__/v5/base/flow.js rename to packages/mobx/__tests__/base/flow.js index b2aa2c0ac5..2676c4d79a 100644 --- a/packages/mobx/__tests__/v5/base/flow.js +++ b/packages/mobx/__tests__/base/flow.js @@ -1,10 +1,10 @@ -import * as mobx from "../../../src/mobx" +import * as mobx from "../../src/mobx" import { flow, FlowCancellationError, isFlowCancellationError, makeObservable -} from "../../../src/mobx" +} from "../../src/mobx" function delay(time, value, shouldThrow = false) { return new Promise((resolve, reject) => { @@ -277,7 +277,6 @@ test("flows can be cancelled - 3 - throw in finally should be caught", done => { err => { expect("" + err).toBe("OOPS") expect(counter.counter).toBe(4) - mobx.configure({ enforceActions: "never" }) d() done() } diff --git a/packages/mobx/__tests__/v5/base/intercept.js b/packages/mobx/__tests__/base/intercept.js similarity index 99% rename from packages/mobx/__tests__/v5/base/intercept.js rename to packages/mobx/__tests__/base/intercept.js index a268ccba5a..81cc6cc76c 100644 --- a/packages/mobx/__tests__/v5/base/intercept.js +++ b/packages/mobx/__tests__/base/intercept.js @@ -1,4 +1,4 @@ -const m = require("../../../src/mobx.ts") +const m = require("../../src/mobx.ts") const intercept = m.intercept test("intercept observable value", () => { diff --git a/packages/mobx/__tests__/v5/base/make-observable.ts b/packages/mobx/__tests__/base/make-observable.ts similarity index 99% rename from packages/mobx/__tests__/v5/base/make-observable.ts rename to packages/mobx/__tests__/base/make-observable.ts index 3e3091d83c..db04237bd2 100644 --- a/packages/mobx/__tests__/v5/base/make-observable.ts +++ b/packages/mobx/__tests__/base/make-observable.ts @@ -1,5 +1,5 @@ -import { isFlow } from "../../../src/api/flow" -import { deepEnhancer } from "../../../src/internal" +import { isFlow } from "../../src/api/flow" +import { deepEnhancer } from "../../src/internal" import { makeObservable, action, @@ -20,7 +20,7 @@ import { override, ObservableSet, ObservableMap -} from "../../../src/mobx" +} from "../../src/mobx" test("makeObservable picks up decorators", () => { class Test { @@ -1543,7 +1543,7 @@ test("@override must override", () => { }) test("makeAutoObservable + production build #2751", () => { - const mobx = require(`../../../dist/mobx.cjs.production.min.js`) + const mobx = require(`../../dist/mobx.cjs.production.min.js`) class Foo { x = "x" constructor() { diff --git a/packages/mobx/__tests__/v5/base/makereactive.js b/packages/mobx/__tests__/base/makereactive.js similarity index 99% rename from packages/mobx/__tests__/v5/base/makereactive.js rename to packages/mobx/__tests__/base/makereactive.js index 2da8670bd9..917427f099 100644 --- a/packages/mobx/__tests__/v5/base/makereactive.js +++ b/packages/mobx/__tests__/base/makereactive.js @@ -1,5 +1,5 @@ -const { isAction, isObservable, isObservableProp } = require("../../../src/mobx") -const mobx = require("../../../src/mobx.ts") +const { isAction, isObservable, isObservableProp } = require("../../src/mobx") +const mobx = require("../../src/mobx.ts") const m = mobx const o = mobx.observable const { makeObservable } = mobx diff --git a/packages/mobx/__tests__/v5/base/map.js b/packages/mobx/__tests__/base/map.js similarity index 96% rename from packages/mobx/__tests__/v5/base/map.js rename to packages/mobx/__tests__/base/map.js index 31507849b9..be93a55716 100644 --- a/packages/mobx/__tests__/v5/base/map.js +++ b/packages/mobx/__tests__/base/map.js @@ -1,6 +1,6 @@ "use strict" -import * as mobx from "../../../src/mobx.ts" +import * as mobx from "../../src/mobx.ts" import { observable, autorun, @@ -11,11 +11,11 @@ import { runInAction, configure, _resetGlobalState -} from "../../../src/mobx.ts" +} from "../../src/mobx.ts" const map = mobx.observable.map const iterall = require("iterall") -import { grabConsole } from "../../v5/utils/test-utils" +import { grabConsole } from "../utils/test-utils" test("map crud", function () { mobx._getGlobalState().mobxGuid = 0 // hmm dangerous reset? @@ -666,38 +666,34 @@ test("work with 'toString' key", () => { test("issue 940, should not be possible to change maps outside strict mode", () => { mobx.configure({ enforceActions: "observed" }) - try { - const m = mobx.observable.map() - const d = mobx.autorun(() => mobx.values(m)) - - expect( - grabConsole(() => { - m.set("x", 1) - }) - ).toMatchInlineSnapshot( - `" [MobX] Since strict-mode is enabled, changing (observed) observable values without using an action is not allowed. Tried to modify: ObservableMap@56.keys()"` - ) - - expect( - grabConsole(() => { - m.set("x", 2) - }) - ).toMatchInlineSnapshot( - `" [MobX] Since strict-mode is enabled, changing (observed) observable values without using an action is not allowed. Tried to modify: ObservableMap@56.x"` - ) - - expect( - grabConsole(() => { - m.delete("x") - }) - ).toMatchInlineSnapshot( - `" [MobX] Since strict-mode is enabled, changing (observed) observable values without using an action is not allowed. Tried to modify: ObservableMap@56.keys()"` - ) - - d() - } finally { - mobx.configure({ enforceActions: "never" }) - } + const m = mobx.observable.map() + const d = mobx.autorun(() => mobx.values(m)) + + expect( + grabConsole(() => { + m.set("x", 1) + }) + ).toMatchInlineSnapshot( + `" [MobX] Since strict-mode is enabled, changing (observed) observable values without using an action is not allowed. Tried to modify: ObservableMap@56.keys()"` + ) + + expect( + grabConsole(() => { + m.set("x", 2) + }) + ).toMatchInlineSnapshot( + `" [MobX] Since strict-mode is enabled, changing (observed) observable values without using an action is not allowed. Tried to modify: ObservableMap@56.x"` + ) + + expect( + grabConsole(() => { + m.delete("x") + }) + ).toMatchInlineSnapshot( + `" [MobX] Since strict-mode is enabled, changing (observed) observable values without using an action is not allowed. Tried to modify: ObservableMap@56.keys()"` + ) + + d() }) test("map getOrInsert", function () { @@ -1402,8 +1398,10 @@ test('initialization should not violate `enforceActions: "always"` - discussion const consoleWarnSpy = jest.spyOn(console, "warn").mockImplementation(() => { throw new Error("console.warn called") }) - configure({ enforceActions: "always" }) - observable(new Map([["x", "x"]])) - _resetGlobalState() - consoleWarnSpy.mockRestore() + try { + configure({ enforceActions: "always" }) + observable(new Map([["x", "x"]])) + } finally { + consoleWarnSpy.mockRestore() + } }) diff --git a/packages/mobx/__tests__/v4/base/nested.js b/packages/mobx/__tests__/base/nested.js similarity index 98% rename from packages/mobx/__tests__/v4/base/nested.js rename to packages/mobx/__tests__/base/nested.js index eacacbda63..0c3ef01cfb 100644 --- a/packages/mobx/__tests__/v4/base/nested.js +++ b/packages/mobx/__tests__/base/nested.js @@ -1,6 +1,6 @@ "use strict" -import { extendObservable, observable, autorun, computed, runInAction } from "../mobx4" +import { extendObservable, observable, autorun, computed, runInAction } from "../../src/mobx" test("nested computeds should not run unnecessary", () => { function Item(name) { diff --git a/packages/mobx/__tests__/v5/base/object-api-proxy.js b/packages/mobx/__tests__/base/object-api-proxy.js similarity index 99% rename from packages/mobx/__tests__/v5/base/object-api-proxy.js rename to packages/mobx/__tests__/base/object-api-proxy.js index 0493844d78..c69ecae8a3 100644 --- a/packages/mobx/__tests__/v5/base/object-api-proxy.js +++ b/packages/mobx/__tests__/base/object-api-proxy.js @@ -1,4 +1,4 @@ -const mobx = require("../../../src/mobx") +const mobx = require("../../src/mobx") const { has, autorun, when, runInAction, reaction, observable } = mobx test("keys should be observable when extending", () => { diff --git a/packages/mobx/__tests__/v5/base/object-api.js b/packages/mobx/__tests__/base/object-api.js similarity index 99% rename from packages/mobx/__tests__/v5/base/object-api.js rename to packages/mobx/__tests__/base/object-api.js index 8fe325a5ca..fe62030509 100644 --- a/packages/mobx/__tests__/v5/base/object-api.js +++ b/packages/mobx/__tests__/base/object-api.js @@ -1,4 +1,4 @@ -const mobx = require("../../../src/mobx") +const mobx = require("../../src/mobx") const { autorun, keys, when, set, remove, values, entries, reaction, observable, has, get } = mobx test("keys should be observable when extending", () => { @@ -621,7 +621,7 @@ test("defineProperty notifies keys observers", () => { expect(Object.getOwnPropertyDescriptor(obj, "foo")).toEqual(desc) expect(mobx.isObservableProp(obj, "foo")).toBe(false) expect(reactionCount).toBe(1) - mobx.remove(obj, 'foo'); + mobx.remove(obj, "foo") expect(obj.hasOwnProperty("foo")).toBe(false) expect(reactionCount).toBe(2) }) diff --git a/packages/mobx/__tests__/v5/base/observables.js b/packages/mobx/__tests__/base/observables.js similarity index 99% rename from packages/mobx/__tests__/v5/base/observables.js rename to packages/mobx/__tests__/base/observables.js index a02424dbad..efaf71fead 100644 --- a/packages/mobx/__tests__/v5/base/observables.js +++ b/packages/mobx/__tests__/base/observables.js @@ -1,6 +1,6 @@ "use strict" -const mobx = require("../../../src/mobx") +const mobx = require("../../src/mobx") const m = mobx const { $mobx, @@ -14,8 +14,8 @@ const { isFlow, isObservableProp } = mobx -const utils = require("../../v5/utils/test-utils") -const { MAX_SPLICE_SIZE, getGlobalState } = require("../../../src/internal") +const utils = require("../utils/test-utils") +const { MAX_SPLICE_SIZE, getGlobalState } = require("../../src/internal") const voidObserver = function () {} @@ -2302,7 +2302,6 @@ describe("`requiresReaction` takes precedence over global `computedRequiresReact }) afterEach(() => { consoleWarnSpy.mockRestore() - mobx._resetGlobalState() }) test("`undefined`", () => { @@ -2336,7 +2335,6 @@ describe("`requiresObservable` takes precedence over global `reactionRequiresObs }) afterEach(() => { consoleWarnSpy.mockRestore() - mobx._resetGlobalState() }) test("`undefined`", () => { @@ -2399,7 +2397,6 @@ test('Observables initialization does not violate `enforceActions: "always"`', ( check(() => mobx.computed(() => 0)) } finally { consoleWarnSpy.mockRestore() - mobx._resetGlobalState() } }) @@ -2418,7 +2415,6 @@ test("enforceAction is respected when changing keys of observable object", () => expect(consoleWarnSpy).toHaveBeenCalled() } finally { consoleWarnSpy.mockRestore() - mobx._resetGlobalState() } }) diff --git a/packages/mobx/__tests__/v5/base/observe.ts b/packages/mobx/__tests__/base/observe.ts similarity index 94% rename from packages/mobx/__tests__/v5/base/observe.ts rename to packages/mobx/__tests__/base/observe.ts index 7f89f36691..2ea1db736e 100644 --- a/packages/mobx/__tests__/v5/base/observe.ts +++ b/packages/mobx/__tests__/base/observe.ts @@ -1,4 +1,4 @@ -import { observable, observe, computed } from "../../../src/mobx" +import { observable, observe, computed } from "../../src/mobx" test("observe object and map properties", () => { const map = observable.map({ a: 1 }) diff --git a/packages/mobx/__tests__/v5/base/proxies.js b/packages/mobx/__tests__/base/proxies.js similarity index 99% rename from packages/mobx/__tests__/v5/base/proxies.js rename to packages/mobx/__tests__/base/proxies.js index 2b2993624c..c5a8a1e93a 100644 --- a/packages/mobx/__tests__/v5/base/proxies.js +++ b/packages/mobx/__tests__/base/proxies.js @@ -10,7 +10,7 @@ import { extendObservable, keys, makeObservable -} from "../../../src/mobx" +} from "../../src/mobx" import { stripAdminFromDescriptors } from "../utils/test-utils" diff --git a/packages/mobx/__tests__/v5/base/reaction.js b/packages/mobx/__tests__/base/reaction.js similarity index 98% rename from packages/mobx/__tests__/v5/base/reaction.js rename to packages/mobx/__tests__/base/reaction.js index 49f6215db6..8d49399572 100644 --- a/packages/mobx/__tests__/v5/base/reaction.js +++ b/packages/mobx/__tests__/base/reaction.js @@ -1,10 +1,10 @@ /** - * @type {typeof import("./../../../src/v5/mobx")} + * @type {typeof import("./../../src/mobx")} */ -const mobx = require("../../../src/mobx.ts") +const mobx = require("../../src/mobx.ts") const reaction = mobx.reaction const $mobx = mobx.$mobx -const utils = require("../../v5/utils/test-utils") +const utils = require("../utils/test-utils") test("basic", () => { const a = mobx.observable.box(1) @@ -294,7 +294,7 @@ test("fireImmediately should not be honored when passed already aborted AbortSig reaction( () => a.get(), - (newValue) => { + newValue => { values.push(newValue) }, { signal: ac.signal, fireImmediately: true } diff --git a/packages/mobx/__tests__/v5/base/set.js b/packages/mobx/__tests__/base/set.js similarity index 96% rename from packages/mobx/__tests__/v5/base/set.js rename to packages/mobx/__tests__/base/set.js index c947e73ae0..45d893547b 100644 --- a/packages/mobx/__tests__/v5/base/set.js +++ b/packages/mobx/__tests__/base/set.js @@ -1,6 +1,6 @@ "use strict" -const mobx = require("../../../src/mobx.ts") +const mobx = require("../../src/mobx.ts") const set = mobx.observable.set const autorun = mobx.autorun const iterall = require("iterall") @@ -475,9 +475,7 @@ describe("Observable Set methods are reactive", () => { }) }) - describe("Observable Set interceptors", () => { - let s = set() beforeEach(() => { @@ -485,37 +483,32 @@ describe("Observable Set interceptors", () => { }) test("Add does not add value if interceptor returned no change", () => { - mobx.intercept(s, (change) => { - if(change.type === 'add' && change.newValue === 2) { - return undefined; + mobx.intercept(s, change => { + if (change.type === "add" && change.newValue === 2) { + return undefined } - return change; + return change }) - s.add(1); - s.add(2); - - expect([...s]).toStrictEqual([1]); - + s.add(1) + s.add(2) + expect([...s]).toStrictEqual([1]) }) test("Add respects newValue from interceptor", () => { - - mobx.intercept(s, (change) => { - if(change.type === 'add' && change.newValue === 2) { - change.newValue = 10; + mobx.intercept(s, change => { + if (change.type === "add" && change.newValue === 2) { + change.newValue = 10 } - return change; + return change }) - s.add(1); - s.add(2); + s.add(1) + s.add(2) expect([...s]).toStrictEqual([1, 10]) }) - - -}) \ No newline at end of file +}) diff --git a/packages/mobx/__tests__/v5/base/spy.js b/packages/mobx/__tests__/base/spy.js similarity index 69% rename from packages/mobx/__tests__/v5/base/spy.js rename to packages/mobx/__tests__/base/spy.js index 16a11bfb53..908fd4ed41 100644 --- a/packages/mobx/__tests__/v5/base/spy.js +++ b/packages/mobx/__tests__/base/spy.js @@ -1,6 +1,6 @@ "use strict" -const mobx = require("../../../src/mobx.ts") -const utils = require("../../v5/utils/test-utils") +const mobx = require("../../src/mobx.ts") +const utils = require("../utils/test-utils") test("spy output", () => { const events = [] @@ -104,38 +104,42 @@ test("spy error", () => { test("spy stop listen from handler, #1459", () => { const stop = mobx.spy(() => stop()) - mobx.spy(() => { }) + mobx.spy(() => {}) doStuff() }) test("bound actions report correct object (discussions/3140)", () => { class AppState { constructor() { - mobx.makeAutoObservable(this, { - actionBound: mobx.action.bound, - }, { autoBind: true }); + mobx.makeAutoObservable( + this, + { + actionBound: mobx.action.bound + }, + { autoBind: true } + ) } - actionBound() { } - autoActionBound() { } + actionBound() {} + autoActionBound() {} } - const appState = new AppState(); - const { actionBound, autoActionBound } = appState; + const appState = new AppState() + const { actionBound, autoActionBound } = appState - let events = []; - const disposeSpy = mobx.spy((event) => { - if (event.type !== 'action') return; - events.push(event); - }); + let events = [] + const disposeSpy = mobx.spy(event => { + if (event.type !== "action") return + events.push(event) + }) try { - actionBound(); + actionBound() expect(events.pop().object).toBe(appState) - autoActionBound(); + autoActionBound() expect(events.pop().object).toBe(appState) } finally { - disposeSpy(); + disposeSpy() } }) @@ -143,31 +147,31 @@ test("computed shouldn't report update unless the value changed #3109", () => { const number = mobx.observable({ value: 0, get isEven() { - return (this.value % 2) === 0; + return this.value % 2 === 0 } }) - const events = []; + const events = [] const disposeSpy = mobx.spy(event => { - if (event.observableKind === 'computed' && event.type === 'update') { - events.push(event); - }; - }); + if (event.observableKind === "computed" && event.type === "update") { + events.push(event) + } + }) - const disposeAutorun = mobx.autorun(() => number.isEven); + const disposeAutorun = mobx.autorun(() => number.isEven) try { - expect(events.pop()).toMatchObject({ oldValue: { cause: null }, newValue: true }); - number.value++; // 1 - expect(events.pop()).toMatchObject({ oldValue: true, newValue: false }); - number.value++; // 2 - expect(events.pop()).toMatchObject({ oldValue: false, newValue: true }); - number.value += 2; // 4 - expect(events.pop()).toBe(undefined); - number.value += 2; // 6 - expect(events.pop()).toBe(undefined); + expect(events.pop()).toMatchObject({ oldValue: { cause: null }, newValue: true }) + number.value++ // 1 + expect(events.pop()).toMatchObject({ oldValue: true, newValue: false }) + number.value++ // 2 + expect(events.pop()).toMatchObject({ oldValue: false, newValue: true }) + number.value += 2 // 4 + expect(events.pop()).toBe(undefined) + number.value += 2 // 6 + expect(events.pop()).toBe(undefined) } finally { - disposeSpy(); - disposeAutorun(); + disposeSpy() + disposeAutorun() } }) diff --git a/packages/mobx/__tests__/v5/base/strict-mode.js b/packages/mobx/__tests__/base/strict-mode.js similarity index 53% rename from packages/mobx/__tests__/v5/base/strict-mode.js rename to packages/mobx/__tests__/base/strict-mode.js index 2fbb885e88..94aa597a06 100644 --- a/packages/mobx/__tests__/v5/base/strict-mode.js +++ b/packages/mobx/__tests__/base/strict-mode.js @@ -1,8 +1,8 @@ /** - * @type {typeof import("../../../src/v5/mobx")} + * @type {typeof import("../../src/mobx")} */ -const mobx = require("../../../src/mobx.ts") -const utils = require("../../v5/utils/test-utils") +const mobx = require("../../src/mobx.ts") +const utils = require("../utils/test-utils") const { makeObservable } = mobx const strictError = @@ -39,7 +39,6 @@ test("actions can modify observed state in strict mode", () => { mobx.observable.box(4) })() - mobx.configure({ enforceActions: "never" }) d() }) @@ -51,15 +50,12 @@ test("actions can modify non-observed state in strict mode", () => { a.set(3) mobx.observable.box(4) })() - - mobx.configure({ enforceActions: "never" }) }) test("reactions cannot modify state in strict mode", () => { const a = mobx.observable.box(3) const b = mobx.observable.box(4) mobx.configure({ enforceActions: "observed" }) - mobx._resetGlobalState() // should preserve strict mode const bd = mobx.autorun(() => { b.get() // make sure it is observed @@ -82,7 +78,6 @@ test("reactions cannot modify state in strict mode", () => { expect(utils.grabConsole(() => a.set(5))).toMatch(strictError) - mobx.configure({ enforceActions: "never" }) d() bd() }) @@ -114,7 +109,6 @@ test("action inside reaction in strict mode can modify state", () => { setA(16) expect(b.get()).toBe(55) - mobx.configure({ enforceActions: "never" }) bd() d() }) @@ -196,237 +190,188 @@ test("strict mode checks", function () { }) ).toMatch(/Side effects like changing state are not allowed at this point/) - mobx._resetGlobalState() d() }) test("enforceActions 'strict' does not allow changing unobserved observables", () => { - try { - mobx.configure({ enforceActions: "always" }) - const x = mobx.observable({ - a: 1, - b: 2 + mobx.configure({ enforceActions: "always" }) + const x = mobx.observable({ + a: 1, + b: 2 + }) + const d = mobx.autorun(() => { + x.a + }) + + expect( + utils.grabConsole(() => { + x.a = 2 }) - const d = mobx.autorun(() => { - x.a + ).toMatch(/Since strict-mode is enabled/) + expect( + utils.grabConsole(() => { + x.b = 2 }) + ).toMatch(/Since strict-mode is enabled/) - expect( - utils.grabConsole(() => { - x.a = 2 - }) - ).toMatch(/Since strict-mode is enabled/) - expect( - utils.grabConsole(() => { - x.b = 2 - }) - ).toMatch(/Since strict-mode is enabled/) - - d() - } finally { - mobx.configure({ enforceActions: "never" }) - } + d() }) test("enforceActions 'strict' should not throw exception while observable array initialization", () => { - try { - mobx.configure({ enforceActions: "always" }) + mobx.configure({ enforceActions: "always" }) - expect(() => { - mobx.observable({ - a: [1, 2] - }) - }).not.toThrow(/Since strict-mode is enabled/) - } finally { - mobx.configure({ enforceActions: "never" }) - } + expect(() => { + mobx.observable({ + a: [1, 2] + }) + }).not.toThrow(/Since strict-mode is enabled/) }) test("warn on unsafe reads of computed", function () { - try { - mobx.configure({ computedRequiresReaction: true }) - const x = mobx.observable({ - y: 3, - get yy() { - return this.y * 2 - } - }) - utils.consoleWarn(() => { - x.yy - }, /being read outside a reactive context/) - } finally { - mobx.configure({ computedRequiresReaction: false }) - } + mobx.configure({ computedRequiresReaction: true }) + const x = mobx.observable({ + y: 3, + get yy() { + return this.y * 2 + } + }) + utils.consoleWarn(() => { + x.yy + }, /being read outside a reactive context/) }) describe("observableRequiresReaction", function () { test("warn on unsafe reads of observable", function () { - try { - mobx.configure({ observableRequiresReaction: true }) - const x = mobx.observable({ - y: 3 - }) - utils.consoleWarn(() => { - x.y - }, /being read outside a reactive context/) - } finally { - mobx.configure({ observableRequiresReaction: false }) - } + mobx.configure({ observableRequiresReaction: true }) + const x = mobx.observable({ + y: 3 + }) + utils.consoleWarn(() => { + x.y + }, /being read outside a reactive context/) }) test("warn on unsafe reads of observable also when there are other subscriptions", function () { - try { - mobx.configure({ observableRequiresReaction: true }) - const x = mobx.observable({ - y: 3 - }) + mobx.configure({ observableRequiresReaction: true }) + const x = mobx.observable({ + y: 3 + }) - const dispose = mobx.autorun(() => x.y) + const dispose = mobx.autorun(() => x.y) - utils.consoleWarn(() => { - x.y - }, /being read outside a reactive context/) + utils.consoleWarn(() => { + x.y + }, /being read outside a reactive context/) - dispose() - } finally { - mobx.configure({ observableRequiresReaction: false }) - } + dispose() }) test("warn on unsafe reads of observable array", function () { - try { - mobx.configure({ observableRequiresReaction: true }) - const x = mobx.observable({ - arr: [1, 2, 3] - }) - utils.consoleWarn(() => { - x.arr[1] - }, /being read outside a reactive context/) - } finally { - mobx.configure({ observableRequiresReaction: false }) - } + mobx.configure({ observableRequiresReaction: true }) + const x = mobx.observable({ + arr: [1, 2, 3] + }) + utils.consoleWarn(() => { + x.arr[1] + }, /being read outside a reactive context/) }) test("don't warn on reads inside a computed", function () { - try { - mobx.configure({ observableRequiresReaction: true }) - const x = mobx.observable({ - y: 1 - }) + mobx.configure({ observableRequiresReaction: true }) + const x = mobx.observable({ + y: 1 + }) - const fooComputed = mobx.computed(() => x.y + 1) + const fooComputed = mobx.computed(() => x.y + 1) - const messages = utils.supressConsole(() => { - const dispose = mobx.autorun(() => fooComputed.get()) - dispose() - }) + const messages = utils.supressConsole(() => { + const dispose = mobx.autorun(() => fooComputed.get()) + dispose() + }) - expect(messages.length).toBe(0) - } finally { - mobx.configure({ observableRequiresReaction: false }) - } + expect(messages.length).toBe(0) }) test("don't warn on autorun tracks invalidation of unbound dependencies", function () { // #2195 - try { - mobx.configure({ observableRequiresReaction: true, enforceActions: "never" }) - const a = mobx.observable.box(0) - const b = mobx.observable.box(0) - const c = mobx.computed(() => a.get() + b.get()) - const values = [] - - mobx.autorun(() => { - values.push(c.get()) - b.set(100) - }) + mobx.configure({ observableRequiresReaction: true, enforceActions: "never" }) + const a = mobx.observable.box(0) + const b = mobx.observable.box(0) + const c = mobx.computed(() => a.get() + b.get()) + const values = [] + + mobx.autorun(() => { + values.push(c.get()) + b.set(100) + }) - const messages = utils.supressConsole(() => { - a.set(1) - }) + const messages = utils.supressConsole(() => { + a.set(1) + }) - expect(messages.length).toBe(0) - } finally { - mobx.configure({ observableRequiresReaction: false }) - } + expect(messages.length).toBe(0) }) test("don't warn on autorun tracks invalidation of unbound dependencies - also with action", function () { // #2195 - try { - mobx.configure({ observableRequiresReaction: true, enforceActions: "never" }) - const a = mobx.observable.box(0) - const b = mobx.observable.box(0) - const c = mobx.computed(() => a.get() + b.get()) - const values = [] - - mobx.autorun(() => { - values.push(c.get()) - b.set(100) - }) + mobx.configure({ observableRequiresReaction: true, enforceActions: "never" }) + const a = mobx.observable.box(0) + const b = mobx.observable.box(0) + const c = mobx.computed(() => a.get() + b.get()) + const values = [] + + mobx.autorun(() => { + values.push(c.get()) + b.set(100) + }) - const messages = utils.supressConsole( - mobx.action(() => { - a.set(1) - }) - ) + const messages = utils.supressConsole( + mobx.action(() => { + a.set(1) + }) + ) - expect(messages.length).toBe(0) - } finally { - mobx.configure({ observableRequiresReaction: false }) - } + expect(messages.length).toBe(0) }) test("don't warn on reads inside an action", function () { - try { - mobx.configure({ observableRequiresReaction: true }) - const x = mobx.observable({ - y: 1 - }) + mobx.configure({ observableRequiresReaction: true }) + const x = mobx.observable({ + y: 1 + }) - const fooAction = mobx.action(() => x.y) + const fooAction = mobx.action(() => x.y) - const messages = utils.supressConsole(() => { - fooAction() - }) + const messages = utils.supressConsole(() => { + fooAction() + }) - expect(messages.length).toBe(0) - } finally { - mobx.configure({ observableRequiresReaction: false }) - } + expect(messages.length).toBe(0) }) test("warn on reads inside a transaction", function () { - try { - mobx.configure({ observableRequiresReaction: true }) - const x = mobx.observable({ - y: 1 - }) + mobx.configure({ observableRequiresReaction: true }) + const x = mobx.observable({ + y: 1 + }) - utils.consoleWarn(() => { - mobx.transaction(() => x.y) - }, /being read outside a reactive context/) - } finally { - mobx.configure({ observableRequiresReaction: false }) - } + utils.consoleWarn(() => { + mobx.transaction(() => x.y) + }, /being read outside a reactive context/) }) }) describe("reactionRequiresObservable", function () { test("warn on reaction creation without dependencies", function () { - try { - mobx.configure({ reactionRequiresObservable: true }) - - utils.consoleWarn(() => { - const dispose = mobx.reaction( - () => "plain value", - newValue => newValue - ) - - dispose() - }, /is created\/updated without reading any observable value/) - } finally { - mobx.configure({ reactionRequiresObservable: false }) - } + mobx.configure({ reactionRequiresObservable: true }) + + utils.consoleWarn(() => { + const dispose = mobx.reaction( + () => "plain value", + newValue => newValue + ) + + dispose() + }, /is created\/updated without reading any observable value/) }) }) @@ -438,5 +383,4 @@ test("#1869", function () { x.set(4) }) ).toMatch("Since strict-mode is enabled") - mobx._resetGlobalState() // should preserve strict mode }) diff --git a/packages/mobx/__tests__/v5/base/tojs.js b/packages/mobx/__tests__/base/tojs.js similarity index 95% rename from packages/mobx/__tests__/v5/base/tojs.js rename to packages/mobx/__tests__/base/tojs.js index bee9e9e47b..c8a01a698b 100644 --- a/packages/mobx/__tests__/v5/base/tojs.js +++ b/packages/mobx/__tests__/base/tojs.js @@ -1,12 +1,11 @@ "use strict" -const mobx = require("../../../src/mobx.ts") +const mobx = require("../../src/mobx.ts") const m = mobx const observable = mobx.observable +const { makeObservable } = mobx test("json1", function () { - mobx._resetGlobalState() - const todos = observable([ { title: "write blog" @@ -360,6 +359,23 @@ test("json cycles when exporting maps as maps", function () { expect(cloneA.e).toBe(cloneA) }) +test("map to JS", () => { + class MyClass { + meta = new Map() + + constructor() { + makeObservable(this, { + meta: observable + }) + this.meta.set("test", { abc: "def", ghi: "jkl" }) + + expect(mobx.toJS(this.meta)).toBeInstanceOf(Map) + } + } + + new MyClass() +}) + describe("recurseEverything set to true", function () { test("recurseEverything is no longer supported", () => { expect(() => mobx.toJS({}, { recurseEverything: true })).toThrowErrorMatchingInlineSnapshot( @@ -369,14 +385,12 @@ describe("recurseEverything set to true", function () { }) test("Does not throw on object with configure({ useProxies: 'ifavailable'}) #2871", () => { - const { useProxies } = mobx._resetGlobalState mobx.configure({ useProxies: "ifavailable" }) expect(() => { const o = mobx.observable({ key: "value" }) const dispose = mobx.autorun(() => mobx.toJS(o)) dispose() }).not.toThrow() - mobx.configure({ useProxies }) }) test("Correctly converts observable objects with computed values", () => { diff --git a/packages/mobx/__tests__/v5/base/trace.ts b/packages/mobx/__tests__/base/trace.ts similarity index 99% rename from packages/mobx/__tests__/v5/base/trace.ts rename to packages/mobx/__tests__/base/trace.ts index 5055fe8a52..bbc8b37c8c 100644 --- a/packages/mobx/__tests__/v5/base/trace.ts +++ b/packages/mobx/__tests__/base/trace.ts @@ -1,6 +1,6 @@ "use strict" -import * as mobx from "../../../src/mobx" +import * as mobx from "../../src/mobx" describe("trace", () => { let consoleLogSpy diff --git a/packages/mobx/__tests__/v5/base/typescript-decorators.ts b/packages/mobx/__tests__/base/typescript-decorators.ts similarity index 99% rename from packages/mobx/__tests__/v5/base/typescript-decorators.ts rename to packages/mobx/__tests__/base/typescript-decorators.ts index dafbcb3603..13b507df1c 100644 --- a/packages/mobx/__tests__/v5/base/typescript-decorators.ts +++ b/packages/mobx/__tests__/base/typescript-decorators.ts @@ -25,8 +25,8 @@ import { makeObservable, flow, flowResult -} from "../../../src/mobx" -import * as mobx from "../../../src/mobx" +} from "../../src/mobx" +import * as mobx from "../../src/mobx" const testFunction = function (a: any) {} @@ -530,8 +530,6 @@ test("267 (typescript) should be possible to declare properties observable outsi makeObservable(this) } } - - configure({ enforceActions: "never" }) }) test("288 atom not detected for object property", () => { diff --git a/packages/mobx/__tests__/v5/base/typescript-tests.ts b/packages/mobx/__tests__/base/typescript-tests.ts similarity index 99% rename from packages/mobx/__tests__/v5/base/typescript-tests.ts rename to packages/mobx/__tests__/base/typescript-tests.ts index 8c17f046dd..e4f55ad3f5 100644 --- a/packages/mobx/__tests__/v5/base/typescript-tests.ts +++ b/packages/mobx/__tests__/base/typescript-tests.ts @@ -32,8 +32,8 @@ import { IValueWillChange, flowResult, IObjectWillChange -} from "../../../src/mobx" -import * as mobx from "../../../src/mobx" +} from "../../src/mobx" +import * as mobx from "../../src/mobx" import { assert, IsExact } from "conditional-type-checks" const v = observable.box(3) @@ -691,8 +691,6 @@ test("267 (typescript) should be possible to declare properties observable outsi }) } } - - configure({ enforceActions: "never" }) }) test("288 atom not detected for object property", () => { diff --git a/packages/mobx/__tests__/v5/base/untracked.ts b/packages/mobx/__tests__/base/untracked.ts similarity index 98% rename from packages/mobx/__tests__/v5/base/untracked.ts rename to packages/mobx/__tests__/base/untracked.ts index 2373100b33..13995b2780 100644 --- a/packages/mobx/__tests__/v5/base/untracked.ts +++ b/packages/mobx/__tests__/base/untracked.ts @@ -1,4 +1,4 @@ -import { autorun, observable, computed, untracked } from "../../../src/mobx" +import { autorun, observable, computed, untracked } from "../../src/mobx" test("untracked 1", () => { let cCalcs = 0, diff --git a/packages/mobx/__tests__/decorators_20223/stage3-decorators.ts b/packages/mobx/__tests__/decorators_20223/stage3-decorators.ts index 9e75b77b98..f24cb07e12 100644 --- a/packages/mobx/__tests__/decorators_20223/stage3-decorators.ts +++ b/packages/mobx/__tests__/decorators_20223/stage3-decorators.ts @@ -134,6 +134,32 @@ test("annotations", () => { expect(order1.aFunction()).toBe(1) }) +test("observable private accessors", () => { + class Store { + @observable accessor #value = 1 + + @computed + get double() { + return this.#value * 2 + } + + increment() { + this.#value++ + } + } + + const store = new Store() + const values: number[] = [] + const dispose = autorun(() => values.push(store.double)) + + store.increment() + dispose() + store.increment() + + expect(values).toEqual([2, 4]) + expect(store.double).toBe(6) +}) + test("box", () => { class Box { @observable accessor uninitialized: any @@ -498,8 +524,6 @@ test("267 (2022.3) should be possible to declare properties observable outside s class Store { @observable accessor timer: number | null = null } - - configure({ enforceActions: "never" }) }) test("288 atom not detected for object property", () => { diff --git a/packages/mobx/__tests__/v5/utils/test-utils.ts b/packages/mobx/__tests__/utils/test-utils.ts similarity index 97% rename from packages/mobx/__tests__/v5/utils/test-utils.ts rename to packages/mobx/__tests__/utils/test-utils.ts index b8ca5665e6..8d639f5012 100644 --- a/packages/mobx/__tests__/v5/utils/test-utils.ts +++ b/packages/mobx/__tests__/utils/test-utils.ts @@ -1,5 +1,5 @@ // @ts-nocheck -import { $mobx } from "../../../src/mobx" +import { $mobx } from "../../src/mobx" export function consoleError(block, regex) { let messages = "" diff --git a/packages/mobx/__tests__/v4/base/__snapshots__/array.js.snap b/packages/mobx/__tests__/v4/base/__snapshots__/array.js.snap deleted file mode 100644 index 36af5050d5..0000000000 --- a/packages/mobx/__tests__/v4/base/__snapshots__/array.js.snap +++ /dev/null @@ -1,17 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`very long arrays can be safely passed to nativeArray.concat #2379 1`] = ` -[MockFunction] { - "calls": [ - [ - "[mobx.array] Attempt to read an array index (10000) that is out of bounds (10000). Please check length first. Out of bound indices will not be tracked by MobX", - ], - ], - "results": [ - { - "type": "return", - "value": undefined, - }, - ], -} -`; diff --git a/packages/mobx/__tests__/v4/base/__snapshots__/extras.js.snap b/packages/mobx/__tests__/v4/base/__snapshots__/extras.js.snap deleted file mode 100644 index a530a78d50..0000000000 --- a/packages/mobx/__tests__/v4/base/__snapshots__/extras.js.snap +++ /dev/null @@ -1,34 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`spy 1 1`] = ` -[ - { - "debugObjectName": "ObservableValue@6", - "newValue": 4, - "observableKind": "value", - "oldValue": 3, - "spyReportStart": true, - "type": "update", - }, - { - "debugObjectName": "ComputedValue@7", - "newValue": 8, - "observableKind": "computed", - "oldValue": 6, - "type": "update", - }, - { - "name": "Autorun@8", - "spyReportStart": true, - "type": "reaction", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, -] -`; diff --git a/packages/mobx/__tests__/v4/base/__snapshots__/object-api.js.snap b/packages/mobx/__tests__/v4/base/__snapshots__/object-api.js.snap deleted file mode 100644 index 1b67fb7d95..0000000000 --- a/packages/mobx/__tests__/v4/base/__snapshots__/object-api.js.snap +++ /dev/null @@ -1,68 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`observe & intercept 1`] = ` -[ - { - "intercept": { - "name": "b", - "newValue": { - "title": "get tea", - }, - "object": { - "a": { - "title": "get coffee", - }, - }, - "type": "add", - }, - }, - { - "intercept": { - "name": "a", - "object": { - "a": { - "title": "get coffee", - }, - }, - "type": "remove", - }, - }, -] -`; - -exports[`observe & intercept 2`] = ` -[ - { - "observe": { - "debugObjectName": "TestObject", - "name": "b", - "newValue": { - "title": "get tea", - }, - "object": { - "b": { - "title": "get tea", - }, - }, - "observableKind": "object", - "type": "add", - }, - }, - { - "observe": { - "debugObjectName": "TestObject", - "name": "a", - "object": { - "b": { - "title": "get tea", - }, - }, - "observableKind": "object", - "oldValue": { - "title": "get coffee", - }, - "type": "remove", - }, - }, -] -`; diff --git a/packages/mobx/__tests__/v4/base/__snapshots__/spy.js.snap b/packages/mobx/__tests__/v4/base/__snapshots__/spy.js.snap deleted file mode 100644 index 44ce6359af..0000000000 --- a/packages/mobx/__tests__/v4/base/__snapshots__/spy.js.snap +++ /dev/null @@ -1,375 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`spy error 1`] = ` -[ - { - "name": "autorun", - "spyReportStart": true, - "type": "reaction", - }, - { - "debugObjectName": "ObservableObject@1.y", - "newValue": 4, - "observableKind": "computed", - "oldValue": CaughtException { - "cause": null, - }, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "debugObjectName": "ObservableObject@1", - "name": "x", - "newValue": 3, - "observableKind": "object", - "oldValue": 2, - "spyReportStart": true, - "type": "update", - }, - { - "debugObjectName": "ObservableObject@1.y", - "newValue": CaughtException { - "cause": "Oops", - }, - "observableKind": "computed", - "oldValue": 4, - "type": "update", - }, - { - "name": "autorun", - "spyReportStart": true, - "type": "reaction", - }, - { - "error": "Oops", - "message": "[mobx] Encountered an uncaught exception that was thrown by a reaction or observer component, in: 'Reaction[autorun]'", - "name": "autorun", - "type": "error", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, -] -`; - -exports[`spy output 1`] = ` -[ - { - "debugObjectName": "ObservableValue@1", - "newValue": "2", - "observableKind": "value", - "type": "create", - }, - { - "debugObjectName": "ObservableValue@1", - "newValue": 3, - "observableKind": "value", - "oldValue": 2, - "spyReportStart": true, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "debugObjectName": "ObservableObject@2", - "name": "c", - "newValue": 4, - "observableKind": "object", - "spyReportStart": true, - "type": "add", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "debugObjectName": "ObservableObject@2", - "name": "c", - "newValue": 5, - "observableKind": "object", - "oldValue": 4, - "spyReportStart": true, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "debugObjectName": "ObservableObject@2", - "name": "d", - "newValue": 6, - "observableKind": "object", - "spyReportStart": true, - "type": "add", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "debugObjectName": "ObservableObject@2", - "name": "d", - "newValue": 7, - "observableKind": "object", - "oldValue": 6, - "spyReportStart": true, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "added": [ - 1, - 2, - ], - "addedCount": 2, - "debugObjectName": "ObservableArray@3", - "index": 0, - "observableKind": "array", - "removed": [], - "removedCount": 0, - "spyReportStart": true, - "type": "splice", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "added": [ - 3, - 4, - ], - "addedCount": 2, - "debugObjectName": "ObservableArray@3", - "index": 2, - "observableKind": "array", - "removed": [], - "removedCount": 0, - "spyReportStart": true, - "type": "splice", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "added": [], - "addedCount": 0, - "debugObjectName": "ObservableArray@3", - "index": 0, - "observableKind": "array", - "removed": [ - 1, - ], - "removedCount": 1, - "spyReportStart": true, - "type": "splice", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "debugObjectName": "ObservableArray@3", - "index": 2, - "newValue": 5, - "observableKind": "array", - "oldValue": 4, - "spyReportStart": true, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "debugObjectName": "ObservableMap@4", - "name": "g", - "newValue": 1, - "observableKind": "map", - "spyReportStart": true, - "type": "add", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "debugObjectName": "ObservableMap@4", - "name": "g", - "observableKind": "map", - "oldValue": 1, - "spyReportStart": true, - "type": "delete", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "debugObjectName": "ObservableMap@4", - "name": "i", - "newValue": 5, - "observableKind": "map", - "spyReportStart": true, - "type": "add", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "debugObjectName": "ObservableMap@4", - "name": "i", - "newValue": 6, - "observableKind": "map", - "oldValue": 5, - "spyReportStart": true, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "name": "Autorun@6", - "spyReportStart": true, - "type": "reaction", - }, - { - "debugObjectName": "ComputedValue@5", - "newValue": 6, - "observableKind": "computed", - "oldValue": CaughtException { - "cause": null, - }, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "debugObjectName": "ObservableValue@1", - "newValue": 4, - "observableKind": "value", - "oldValue": 3, - "spyReportStart": true, - "type": "update", - }, - { - "debugObjectName": "ComputedValue@5", - "newValue": 8, - "observableKind": "computed", - "oldValue": 6, - "type": "update", - }, - { - "name": "Autorun@6", - "spyReportStart": true, - "type": "reaction", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "debugObjectName": "ObservableValue@1", - "newValue": 5, - "observableKind": "value", - "oldValue": 4, - "spyReportStart": true, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "debugObjectName": "ObservableValue@1", - "newValue": 6, - "observableKind": "value", - "oldValue": 5, - "spyReportStart": true, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "debugObjectName": "ComputedValue@5", - "newValue": 12, - "observableKind": "computed", - "oldValue": 8, - "type": "update", - }, - { - "name": "Autorun@6", - "spyReportStart": true, - "type": "reaction", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "arguments": [ - 7, - ], - "name": "myTestAction", - "spyReportStart": true, - "type": "action", - }, - { - "debugObjectName": "ObservableValue@1", - "newValue": 7, - "observableKind": "value", - "oldValue": 6, - "spyReportStart": true, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "debugObjectName": "ComputedValue@5", - "newValue": 14, - "observableKind": "computed", - "oldValue": 12, - "type": "update", - }, - { - "name": "Autorun@6", - "spyReportStart": true, - "type": "reaction", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, -] -`; diff --git a/packages/mobx/__tests__/v4/base/array.js b/packages/mobx/__tests__/v4/base/array.js deleted file mode 100644 index 955409527e..0000000000 --- a/packages/mobx/__tests__/v4/base/array.js +++ /dev/null @@ -1,849 +0,0 @@ -"use strict" - -const { LegacyObservableArray } = require("../../../src/internal") -const mobx = require("../mobx4") -const { observable, _getAdministration, reaction, makeObservable } = mobx -const iterall = require("iterall") - -let consoleWarnMock -afterEach(() => { - consoleWarnMock?.mockRestore() -}) - -expect.addEqualityTesters([ - function (a, b, ...rest) { - if (a instanceof LegacyObservableArray || b instanceof LegacyObservableArray) { - return this.equals([...a], [...b], ...rest) - } - return undefined - } -]) - -test("test1", function () { - const a = observable.array([]) - expect(a.length).toBe(0) - expect(Object.keys(a)).toEqual([]) - expect(a.slice()).toEqual([]) - - a.push(1) - expect(a.length).toBe(1) - expect(a.slice()).toEqual([1]) - - a[1] = 2 - expect(a.length).toBe(2) - expect(a.slice()).toEqual([1, 2]) - - const sum = mobx.computed(function () { - return ( - -1 + - a.reduce(function (a, b) { - return a + b - }, 1) - ) - }) - - expect(sum.get()).toBe(3) - - a[1] = 3 - expect(a.length).toBe(2) - expect(a.slice()).toEqual([1, 3]) - expect(sum.get()).toBe(4) - - a.splice(1, 1, 4, 5) - expect(a.length).toBe(3) - expect(a.slice()).toEqual([1, 4, 5]) - expect(sum.get()).toBe(10) - - a.replace([2, 4]) - expect(sum.get()).toBe(6) - - a.splice(1, 1) - expect(sum.get()).toBe(2) - expect(a.slice()).toEqual([2]) - - a.spliceWithArray(0, 0, [4, 3]) - expect(sum.get()).toBe(9) - expect(a.slice()).toEqual([4, 3, 2]) - - a.clear() - expect(sum.get()).toBe(0) - expect(a.slice()).toEqual([]) - - a.length = 4 - expect(isNaN(sum.get())).toBe(true) - expect(a.length).toEqual(4) - - expect(a.slice()).toEqual([undefined, undefined, undefined, undefined]) - - a.replace([1, 2, 2, 4]) - expect(sum.get()).toBe(9) - a.length = 4 - expect(sum.get()).toBe(9) - - a.length = 2 - expect(sum.get()).toBe(3) - expect(a.slice()).toEqual([1, 2]) - - expect(a.reverse()).toEqual([2, 1]) - expect(a).toEqual([2, 1]) - expect(a.slice()).toEqual([2, 1]) - - a.unshift(3) - expect(a.sort()).toEqual([1, 2, 3]) - expect(a.slice()).toEqual([1, 2, 3]) - - expect(JSON.stringify(a)).toBe("[1,2,3]") - - expect(a[1]).toBe(2) - a[2] = 4 - expect(a[2]).toBe(4) - - // t.deepEqual(Object.keys(a), ['0', '1', '2']); // ideally.... - expect(Object.keys(a)).toEqual([]) -}) - -test("array should support iterall / iterable ", () => { - const a = observable([1, 2, 3]) - - expect(iterall.isIterable(a)).toBe(true) - - const values = [] - iterall.forEach(a, v => values.push(v)) - - expect(values).toEqual([1, 2, 3]) - - let iter = iterall.getIterator(a) - expect(iter.next()).toEqual({ value: 1, done: false }) - expect(iter.next()).toEqual({ value: 2, done: false }) - expect(iter.next()).toEqual({ value: 3, done: false }) - expect(iter.next()).toEqual({ value: undefined, done: true }) - - a.replace([]) - iter = iterall.getIterator(a) - expect(iter.next()).toEqual({ value: undefined, done: true }) -}) - -test("find(findIndex) and remove", function () { - const a = mobx.observable([10, 20, 20]) - let idx = -1 - function predicate(item, index) { - if (item === 20) { - idx = index - return true - } - return false - } - - expect(a.find(predicate)).toBe(20) - expect(idx).toBe(1) - expect(a.findIndex(predicate)).toBe(1) - expect(a.find(predicate, null)).toBe(20) - expect(idx).toBe(1) - idx = -1 - - expect(a.remove(20)).toBe(true) - expect(a.find(predicate)).toBe(20) - expect(idx).toBe(1) - expect(a.findIndex(predicate)).toBe(1) - idx = -1 - expect(a.remove(20)).toBe(true) - expect(a.find(predicate)).toBe(undefined) - expect(idx).toBe(-1) - expect(a.findIndex(predicate)).toBe(-1) - - expect(a.remove(20)).toBe(false) -}) - -test("findLast(findLastIndex) and remove", function () { - const a = mobx.observable([10, 20, 20]) - let idx = -1 - function predicate(item, index) { - if (item === 20) { - idx = index - return true - } - return false - } - ;[].findLastIndex; - expect(a.findLast(predicate)).toBe(20) - expect(a.findLastIndex(predicate)).toBe(2) - expect(a.findLast(predicate)).toBe(20) - - expect(a.remove(20)).toBe(true) - expect(a.find(predicate)).toBe(20) - expect(idx).toBe(1) - expect(a.findIndex(predicate)).toBe(1) - idx = -1 - expect(a.remove(20)).toBe(true) - expect(a.findLast(predicate)).toBe(undefined) - expect(idx).toBe(-1) - expect(a.findLastIndex(predicate)).toBe(-1) - - expect(a.remove(20)).toBe(false) -}) - -test("concat should automatically slice observable arrays, #260", () => { - const a1 = mobx.observable([1, 2]) - const a2 = mobx.observable([3, 4]) - expect(a1.concat(a2)).toEqual([1, 2, 3, 4]) -}) - -test("observe", function () { - const ar = mobx.observable([1, 4]) - const buf = [] - const disposer = mobx.observe( - ar, - function (changes) { - buf.push(changes) - }, - true - ) - - ar[1] = 3 // 1,3 - ar[2] = 0 // 1, 3, 0 - ar.shift() // 3, 0 - ar.push(1, 2) // 3, 0, 1, 2 - ar.splice(1, 2, 3, 4) // 3, 3, 4, 2 - expect(ar.slice()).toEqual([3, 3, 4, 2]) - ar.splice(6) - ar.splice(6, 2) - ar.replace(["a"]) - ar.pop() - ar.pop() // does not fire anything - - // check the object param - buf.forEach(function (change) { - expect(change.object).toBe(ar) - delete change.object - expect(change.observableKind).toBe("array") - delete change.observableKind - delete change.debugObjectName - }) - - const result = [ - { type: "splice", index: 0, addedCount: 2, removed: [], added: [1, 4], removedCount: 0 }, - { type: "update", index: 1, oldValue: 4, newValue: 3 }, - { type: "splice", index: 2, addedCount: 1, removed: [], added: [0], removedCount: 0 }, - { type: "splice", index: 0, addedCount: 0, removed: [1], added: [], removedCount: 1 }, - { type: "splice", index: 2, addedCount: 2, removed: [], added: [1, 2], removedCount: 0 }, - { - type: "splice", - index: 1, - addedCount: 2, - removed: [0, 1], - added: [3, 4], - removedCount: 2 - }, - { - type: "splice", - index: 0, - addedCount: 1, - removed: [3, 3, 4, 2], - added: ["a"], - removedCount: 4 - }, - { type: "splice", index: 0, addedCount: 0, removed: ["a"], added: [], removedCount: 1 } - ] - - expect(buf).toEqual(result) - - disposer() - ar[0] = 5 - expect(buf).toEqual(result) -}) - -test("array modification1", function () { - const a = mobx.observable([1, 2, 3]) - const r = a.splice(-10, 5, 4, 5, 6) - expect(a.slice()).toEqual([4, 5, 6]) - expect(r).toEqual([1, 2, 3]) -}) - -test("serialize", function () { - let a = [1, 2, 3] - const m = mobx.observable(a) - - expect(JSON.stringify(m)).toEqual(JSON.stringify(a)) - expect(a).toEqual(m.slice()) - - a = [4] - m.replace(a) - expect(JSON.stringify(m)).toEqual(JSON.stringify(a)) - expect(a).toEqual(m.toJSON()) -}) - -test("array modification functions", function () { - const ars = [[], [1, 2, 3]] - const funcs = ["push", "pop", "shift", "unshift"] - funcs.forEach(function (f) { - ars.forEach(function (ar) { - const a = ar.slice() - const b = mobx.observable(a) - const res1 = a[f](4) - const res2 = b[f](4) - expect(res1).toEqual(res2) - expect(a).toEqual(b.slice()) - }) - }) -}) - -test("array modifications", function () { - const a2 = mobx.observable([]) - const inputs = [undefined, -10, -4, -3, -1, 0, 1, 3, 4, 10] - const arrays = [ - [], - [1], - [1, 2, 3, 4], - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], - [1, undefined], - [undefined] - ] - for (let i = 0; i < inputs.length; i++) - for (let j = 0; j < inputs.length; j++) - for (let k = 0; k < arrays.length; k++) - for (let l = 0; l < arrays.length; l++) { - ;[ - "array mod: [", - arrays[k].toString(), - "] i: ", - inputs[i], - " d: ", - inputs[j], - " [", - arrays[l].toString(), - "]" - ].join(" ") - const a1 = arrays[k].slice() - a2.replace(a1) - const res1 = a1.splice.apply(a1, [inputs[i], inputs[j]].concat(arrays[l])) - const res2 = a2.splice.apply(a2, [inputs[i], inputs[j]].concat(arrays[l])) - expect(a1.slice()).toEqual(a2.slice()) - expect(res1).toEqual(res2) - expect(a1.length).toBe(a2.length) - } -}) - -test("is array", function () { - const x = mobx.observable([]) - expect(x instanceof Array).toBe(true) - - // would be cool if this would return true... - expect(Array.isArray(x)).toBe(false) -}) - -test("stringifies same as ecma array", function () { - const x = mobx.observable([]) - expect(x instanceof Array).toBe(true) - - // would be cool if these two would return true... - expect(x.toString()).toBe("") - expect(x.toLocaleString()).toBe("") - x.push(1, 2) - expect(x.toString()).toBe("1,2") - expect(x.toLocaleString()).toBe("1,2") -}) - -test("observes when stringified", function () { - const x = mobx.observable([]) - let c = 0 - mobx.autorun(function () { - x.toString() - c++ - }) - x.push(1) - expect(c).toBe(2) -}) - -test("observes when stringified to locale", function () { - const x = mobx.observable([]) - let c = 0 - mobx.autorun(function () { - x.toLocaleString() - c++ - }) - x.push(1) - expect(c).toBe(2) -}) - -test("react to sort changes", function () { - const x = mobx.observable([4, 2, 3]) - const sortedX = mobx.computed(function () { - return x.slice().sort() - }) - let sorted - - mobx.autorun(function () { - sorted = sortedX.get() - }) - - expect(x.slice()).toEqual([4, 2, 3]) - expect(sorted).toEqual([2, 3, 4]) - x.push(1) - expect(x.slice()).toEqual([4, 2, 3, 1]) - expect(sorted).toEqual([1, 2, 3, 4]) - x.shift() - expect(x.slice()).toEqual([2, 3, 1]) - expect(sorted).toEqual([1, 2, 3]) -}) - -test("autoextend buffer length", function () { - const ar = observable(new Array(1000)) - let changesCount = 0 - mobx.observe(ar, () => ++changesCount) - - ar[ar.length] = 0 - ar.push(0) - - expect(changesCount).toBe(2) -}) - -test("array exposes correct keys", () => { - const keys = [] - const ar = observable([1, 2]) - for (const key in ar) keys.push(key) - - expect(keys).toEqual([]) -}) - -test("accessing out of bound values throws", () => { - const a = mobx.observable([]) - - let warns = 0 - const baseWarn = console.warn - console.warn = () => { - warns++ - } - - a[0] // out of bounds - a[1] // out of bounds - - expect(warns).toBe(2) - - expect(() => (a[0] = 3)).not.toThrow() - expect(() => (a[2] = 4)).toThrow(/Index out of bounds, 2 is larger than 1/) - - console.warn = baseWarn -}) - -test("replace can handle large arrays", () => { - const a = mobx.observable([]) - const b = [] - b.length = 1000 * 1000 - expect(() => { - a.replace(b) - }).not.toThrow() - - expect(() => { - a.spliceWithArray(0, 0, b) - }).not.toThrow() -}) - -test("can iterate arrays", () => { - const x = mobx.observable([]) - const y = [] - const d = mobx.reaction( - () => Array.from(x), - items => y.push(items), - { fireImmediately: true } - ) - - x.push("a") - x.push("b") - expect(y).toEqual([[], ["a"], ["a", "b"]]) - d() -}) - -test("array is concat spreadable, #1395", () => { - const x = mobx.observable([1, 2, 3, 4]) - const y = [5].concat(x) - expect(y.length).toBe(2) // Should become 5 in MobX 5 - expect(y).toEqual([5, x]) // should become [5, 1,2,3,4] in MobX 5 -}) - -test("array is spreadable, #1395", () => { - const x = mobx.observable([1, 2, 3, 4]) - expect([5, ...x]).toEqual([5, 1, 2, 3, 4]) - - const y = mobx.observable([]) - expect([5, ...y]).toEqual([5]) -}) - -test("array supports toStringTag, #1490", () => { - // N.B. on old environments this requires polyfils for these symbols *and* Object.prototype.toString. - // core-js provides both - const a = mobx.observable([]) - expect(Object.prototype.toString.call(a)).toBe("[object Array]") -}) - -test("concats correctly #1667", () => { - const x = observable({ data: [] }) - - function generate(count) { - const d = [] - for (let i = 0; i < count; i++) d.push({}) - return d - } - - x.data = generate(10000) - const first = x.data[0] - - x.data = x.data.concat(generate(1000)) - expect(x.data[0]).toBe(first) - expect(x.data.length).toBe(11000) -}) - -test("dehances last value on shift/pop", () => { - const x1 = observable([3, 5]) - _getAdministration(x1).dehancer = value => { - return value * 2 - } - expect(x1.shift()).toBe(6) - expect(x1.shift()).toBe(10) - - const x2 = observable([3, 5]) - _getAdministration(x2).dehancer = value => { - return value * 2 - } - expect(x2.pop()).toBe(10) - expect(x2.pop()).toBe(6) -}) - -test("#2044 symbol key on array", () => { - const x = observable([1, 2]) - const s = Symbol("test") - x[s] = 3 - expect(x[s]).toBe(3) - - let reacted = false - const d = reaction( - () => x[s], - () => { - reacted = true - } - ) - - x[s] = 4 - expect(x[s]).toBe(4) - - // although x[s] can be stored, it won't be reactive! - expect(reacted).toBe(false) - d() -}) - -test("#2044 non-symbol key on array", () => { - const x = observable([1, 2]) - const s = "test" - x[s] = 3 - expect(x[s]).toBe(3) - - let reacted = false - const d = reaction( - () => x[s], - () => { - reacted = true - } - ) - - x[s] = 4 - expect(x[s]).toBe(4) - - // although x[s] can be stored, it won't be reactive! - expect(reacted).toBe(false) - d() -}) - -describe("extended array prototype", () => { - const extensionKey = "__extension" - - // A single setup/teardown for all tests because we're pretending to do a - // singular global (dirty) change to the "environment". - beforeAll(() => { - Array.prototype[extensionKey] = () => {} - }) - afterAll(() => { - delete Array.prototype[extensionKey] - }) - - test("creating an observable should work", () => { - mobx.observable({ b: "b" }) - }) - - test("extending an observable should work", () => { - const a = { b: "b" } - mobx.extendObservable(a, {}) - }) -}) - -test("reproduce #2021", () => { - expect.assertions(1) - try { - Array.prototype.extension = function () { - console.log("I'm the extension!", this.length) - } - - class Test { - data = null - - constructor() { - makeObservable(this, { - data: observable - }) - } - } - - const test = new Test() - - mobx.autorun(() => { - if (test.data) expect(test.data.someStr).toBe("123") - }) - - test.data = { someStr: "123" } - } finally { - delete Array.prototype.extension - } -}) - -test("correct array should be passed to callbacks #2326", () => { - const array = observable([1, 2, 3]) - - function callback() { - const lastArg = arguments[arguments.length - 1] - expect(lastArg).toBe(array) - } - ;[ - "every", - "filter", - "find", - "findIndex", - "findLast", - "findLastIndex", - "flatMap", - "forEach", - "map", - "reduce", - "reduceRight", - "some" - ].forEach(method => { - if (Array.prototype[method]) array[method](callback) - else console.warn("SKIPPING: " + method) - }) -}) - -test("very long arrays can be safely passed to nativeArray.concat #2379", () => { - const nativeArray = ["a", "b"] - const longNativeArray = [...Array(10000).keys()] - const longObservableArray = observable(longNativeArray) - expect(longObservableArray.length).toBe(10000) - expect(longObservableArray).toEqual(longNativeArray) - expect(longObservableArray[9000]).toBe(longNativeArray[9000]) - expect(longObservableArray[9999]).toBe(longNativeArray[9999]) - consoleWarnMock = jest.spyOn(console, "warn").mockImplementation(() => {}) - expect(longObservableArray[10000]).toBe(longNativeArray[10000]) - expect(consoleWarnMock).toMatchSnapshot() - - const expectedArray = nativeArray.concat(longNativeArray) - const actualArray = nativeArray.concat(longObservableArray.slice()) // NOTE: in MobX4 slice is needed - - expect(actualArray).toEqual(expectedArray) - - const anotherArray = [0, 1, 2, 3, 4, 5] - const observableArray = observable(anotherArray) - const r1 = anotherArray.splice(2, 2, ...longNativeArray) - const r2 = observableArray.splice(2, 2, ...longNativeArray) - expect(r2).toEqual(r1) - expect(observableArray).toEqual(anotherArray) -}) - -describe("dehances", () => { - function supressConsoleWarn(fn) { - const { warn } = console - console.warn = () => {} - const result = fn() - console.warn = warn - return result - } - - const dehancer = thing => { - // Dehance only objects of a proper type - if (thing && typeof thing === "object" && thing.hasOwnProperty("value")) { - return thing.value - } - // Support nested arrays - if (Array.isArray(thing)) { - // If array has own dehancer it's still applied prior to ours. - // It doesn't matter how many dehancers we apply, - // if they ignore unknown types. - return thing.map(dehancer) - } - // Ignore unknown types - return thing - } - - let enhanced, dehanced, array - - beforeEach(() => { - enhanced = [{ value: 1 }, { value: 2 }, { value: 3 }] - dehanced = enhanced.map(dehancer) - array = observable(enhanced) - mobx._getAdministration(array).dehancer = dehancer - }) - - test("slice", () => { - expect(array.slice()).toEqual(dehanced.slice()) - }) - - test("filter", () => { - const predicate = value => value === 2 - expect(array.filter(predicate)).toEqual(dehanced.filter(predicate)) - }) - - test("concat", () => { - expect(array.concat(4)).toEqual(dehanced.concat(4)) - }) - - test("entries", () => { - expect([...array.entries()]).toEqual([...dehanced.entries()]) - }) - - test("every", () => { - array.every((value, index) => { - expect(value).toEqual(dehanced[index]) - return true - }) - }) - - test("find", () => { - const predicate = value => value === 2 - expect(array.find(predicate)).toEqual(dehanced.find(predicate)) - }) - - test("forEach", () => { - array.forEach((value, index) => { - expect(value).toEqual(dehanced[index]) - }) - }) - - test("includes", () => { - expect(array.includes(2)).toEqual(dehanced.includes(2)) - }) - - test("indexOf", () => { - expect(array.indexOf(2)).toEqual(dehanced.indexOf(2)) - }) - - test("join", () => { - expect(array.join()).toEqual(dehanced.join()) - }) - - test("lastIndexOf", () => { - expect(array.lastIndexOf(2)).toEqual(dehanced.lastIndexOf(2)) - }) - - test("map", () => { - array.map((value, index) => { - expect(value).toEqual(dehanced[index]) - return value - }) - }) - - test("pop", () => { - expect(array.pop()).toEqual(dehanced.pop()) - }) - - test("reduce", () => { - array.reduce((_, value, index) => { - expect(value).toEqual(dehanced[index]) - }) - }) - - test("reduceRight", () => { - array.reduceRight((_, value, index) => { - expect(value).toEqual(dehanced[index]) - }) - }) - - test("reverse", () => { - const reversedArray = supressConsoleWarn(() => array.reverse()) - expect(reversedArray).toEqual(dehanced.reverse()) - }) - - test("shift", () => { - expect(array.shift()).toEqual(dehanced.shift()) - }) - - test("some", () => { - array.some((value, index) => { - expect(value).toEqual(dehanced[index]) - return false - }) - }) - - test("splice", () => { - expect(array.splice(1, 2)).toEqual(dehanced.splice(1, 2)) - }) - - test("sort", () => { - const comparator = (a, b) => { - expect(typeof a).toEqual("number") - expect(typeof b).toEqual("number") - return b > a - } - const sortedArray = supressConsoleWarn(() => array.sort(comparator)) - expect(sortedArray).toEqual(dehanced.sort(comparator)) - }) - - test("values", () => { - expect([...array.values()]).toEqual([...dehanced.values()]) - }) - - test("toReversed", () => { - expect(array.toReversed()).toEqual(dehanced.toReversed()) - }) - - test("toSorted", () => { - expect(array.toSorted()).toEqual(dehanced.toSorted()) - }) - - test("toSorted with args", () => { - expect(array.toSorted((a, b) => a - b)).toEqual(dehanced.toSorted((a, b) => a - b)) - }) - - test("toSpliced", () => { - expect(array.toSpliced(1, 2)).toEqual(dehanced.toSpliced(1, 2)) - }) - - test("with", () => { - expect(array.with(1, 5)).toEqual(dehanced.with(1, 5)) - }) - - test("at", () => { - expect(array.at(1)).toEqual(dehanced.at(1)) - expect(array.at(-1)).toEqual(dehanced.at(-1)) - }) - - test("flat/flatMap", () => { - // not supported in V4 - }) -}) - -test("reduce without initial value #2432", () => { - const array = [1, 2, 3] - const observableArray = observable(array) - - const arrayReducerArgs = [] - const observableArrayReducerArgs = [] - - const arraySum = array.reduce((...args) => { - arrayReducerArgs.push(args) - return args[0] + args[1] - }) - - const observableArraySum = observableArray.reduce((...args) => { - observableArrayReducerArgs.push(args) - return args[0] + args[1] - }) - - expect(arraySum).toEqual(1 + 2 + 3) - expect(observableArraySum).toEqual(arraySum) - expect(arrayReducerArgs).toEqual(observableArrayReducerArgs) -}) diff --git a/packages/mobx/__tests__/v4/base/autorun.js b/packages/mobx/__tests__/v4/base/autorun.js deleted file mode 100644 index ed809ad2c8..0000000000 --- a/packages/mobx/__tests__/v4/base/autorun.js +++ /dev/null @@ -1,147 +0,0 @@ -/** - * @type {typeof import("./../../../src/v4/mobx")} - */ -const mobx = require("../mobx4") -const utils = require("../../v5/utils/test-utils") - -test("autorun passes Reaction as an argument to view function", function () { - const a = mobx.observable.box(1) - const values = [] - - mobx.autorun(r => { - expect(typeof r.dispose).toBe("function") - if (a.get() === "pleaseDispose") r.dispose() - values.push(a.get()) - }) - - a.set(2) - a.set(2) - a.set("pleaseDispose") - a.set(3) - a.set(4) - - expect(values).toEqual([1, 2, "pleaseDispose"]) -}) - -test("autorun can be disposed on first run", function () { - const a = mobx.observable.box(1) - const values = [] - - mobx.autorun(r => { - r.dispose() - values.push(a.get()) - }) - - a.set(2) - - expect(values).toEqual([1]) -}) - -test("autorun warns when passed an action", function () { - const action = mobx.action(() => {}) - expect.assertions(1) - expect(() => mobx.autorun(action)).toThrow(/Autorun does not accept actions/) -}) - -test("autorun batches automatically", function () { - let runs = 0 - let a1runs = 0 - let a2runs = 0 - - const x = mobx.observable({ - a: 1, - b: 1, - c: 1, - get d() { - runs++ - return this.c + this.b - } - }) - - const d1 = mobx.autorun(() => { - a1runs++ - x.d // read - }) - - const d2 = mobx.autorun(() => { - a2runs++ - x.b = x.a - x.c = x.a - }) - - expect(a1runs).toBe(1) - expect(a2runs).toBe(1) - expect(runs).toBe(1) - - x.a = 17 - - expect(a1runs).toBe(2) - expect(a2runs).toBe(2) - expect(runs).toBe(2) - - d1() - d2() -}) - -test("autorun tracks invalidation of unbound dependencies", function () { - const a = mobx.observable.box(0) - const b = mobx.observable.box(0) - const c = mobx.computed(() => a.get() + b.get()) - const values = [] - - mobx.autorun(() => { - values.push(c.get()) - b.set(100) - }) - - a.set(1) - expect(values).toEqual([0, 100, 101]) -}) - -test("when effect is an action", function (done) { - const a = mobx.observable.box(0) - - mobx.configure({ enforceActions: "observed" }) - mobx.when( - () => a.get() === 1, - () => { - a.set(2) - - mobx.configure({ enforceActions: "never" }) - done() - }, - { timeout: 1 } - ) - - mobx.runInAction(() => { - a.set(1) - }) -}) - -describe("autorun opts requiresObservable", () => { - test("warn when no observable", () => { - utils.consoleWarn(() => { - const disposer = mobx.autorun(() => 2, { - requiresObservable: true - }) - - disposer() - }, /is created\/updated without reading any observable value/) - }) - - test("Don't warn when observable", () => { - const obsr = mobx.observable({ - x: 1 - }) - - const messages = utils.supressConsole(() => { - const disposer = mobx.autorun(() => obsr.x, { - requiresObservable: true - }) - - disposer() - }) - - expect(messages.length).toBe(0) - }) -}) diff --git a/packages/mobx/__tests__/v4/base/extendObservable.js b/packages/mobx/__tests__/v4/base/extendObservable.js deleted file mode 100644 index ae26e81607..0000000000 --- a/packages/mobx/__tests__/v4/base/extendObservable.js +++ /dev/null @@ -1,163 +0,0 @@ -// @ts-check - -import { - action, - autorun, - isObservable, - isObservableProp, - isComputedProp, - isAction, - extendObservable -} from "../mobx4" - -test("extendObservable should work", function () { - class Box { - // @ts-ignore - uninitialized - height = 20 - sizes = [2] - someFunc = function () { - return 2 - } - get width() { - return ( - this.undeclared * - this.height * - this.sizes.length * - this.someFunc() * - (this.uninitialized ? 2 : 1) - ) - } - addSize() { - // @ts-ignore - this.sizes.push([3]) - // @ts-ignore - this.sizes.push([4]) - } - constructor() { - this.undeclared = 1 - } - } - - const box = new Box() - - extendObservable(box, { - height: 20, - sizes: [2], - get someFunc() { - return 2 - }, - width: 40 - }) - - expect(isObservableProp(box, "height")).toBe(true) - expect(isObservableProp(box, "sizes")).toBe(true) - expect(isObservable(box.sizes)).toBe(true) - expect(isObservableProp(box, "someFunc")).toBe(true) - expect(isComputedProp(box, "someFunc")).toBe(true) - expect(isObservableProp(box, "width")).toBe(true) - - const ar = [] - - autorun(() => { - ar.push(box.width) - }) - - expect(ar.slice()).toEqual([40]) -}) - -test("extendObservable should work with plain object", function () { - const box = { - /** @type {boolean | undefined} */ - uninitialized: undefined, - height: 20, - sizes: [2], - someFunc: function () { - return 2 - }, - get width() { - return ( - this.undeclared * - this.height * - this.sizes.length * - this.someFunc() * - (this.uninitialized ? 2 : 1) - ) - }, - addSize() { - // @ts-ignore - this.sizes.push([3]) - // @ts-ignore - this.sizes.push([4]) - } - } - - box.undeclared = 1 - - extendObservable(box, { - height: 20, - sizes: [2], - get someFunc() { - return 2 - }, - width: 40 - }) - - expect(isObservableProp(box, "height")).toBe(true) - expect(isObservableProp(box, "sizes")).toBe(true) - expect(isObservable(box.sizes)).toBe(true) - expect(isObservableProp(box, "someFunc")).toBe(true) - expect(isComputedProp(box, "someFunc")).toBe(true) - expect(isObservableProp(box, "width")).toBe(true) - - const ar = [] - - autorun(() => { - ar.push(box.width) - }) - - expect(ar.slice()).toEqual([40]) -}) - -test("extendObservable should apply specified decorators", function () { - const box = { - /** @type {boolean | undefined} */ - uninitialized: undefined, - height: 20, - sizes: [2], - someFunc: function () { - return 2 - }, - get width() { - return ( - this.undeclared * - this.height * - this.sizes.length * - this.someFunc() * - (this.uninitialized ? 2 : 1) - ) - }, - addSize() { - // @ts-ignore - this.sizes.push([3]) - // @ts-ignore - this.sizes.push([4]) - } - } - - box.undeclared = 1 - - extendObservable( - box, - { - someFunc: function () { - return 2 - } - }, - // @ts-ignore - { someFunc: action } - ) - - expect(isAction(box.someFunc)).toBe(true) - expect(box.someFunc()).toEqual(2) -}) diff --git a/packages/mobx/__tests__/v4/base/extras.js b/packages/mobx/__tests__/v4/base/extras.js deleted file mode 100644 index 51306bea85..0000000000 --- a/packages/mobx/__tests__/v4/base/extras.js +++ /dev/null @@ -1,414 +0,0 @@ -const mobx = require("../mobx4") -const m = mobx -const { $mobx } = mobx - -test("treeD", function () { - m._resetGlobalState() - mobx._getGlobalState().mobxGuid = 0 - const a = m.observable.box(3) - const aName = "ObservableValue@1" - - const dtree = m.getDependencyTree - expect(dtree(a)).toEqual({ - name: aName - }) - - const b = m.computed(() => a.get() * a.get()) - const bName = "ComputedValue@2" - expect(dtree(b)).toEqual({ - name: bName - // no dependencies yet, since it isn't observed yet - }) - - const c = m.autorun(() => b.get()) - const cName = "Autorun@3" - expect(dtree(c[$mobx])).toEqual({ - name: cName, - dependencies: [ - { - name: bName, - dependencies: [ - { - name: aName - } - ] - } - ] - }) - - expect(aName !== bName).toBeTruthy() - expect(bName !== cName).toBeTruthy() - - expect(m.getObserverTree(a)).toEqual({ - name: aName, - observers: [ - { - name: bName, - observers: [ - { - name: cName - } - ] - } - ] - }) - - const x = mobx.observable.map({ temperature: 0 }) - const d = mobx.autorun(function () { - x.keys() - if (x.has("temperature")) x.get("temperature") - x.has("absent") - }) - - expect(m.getDependencyTree(d[$mobx])).toEqual({ - name: "Autorun@5", - dependencies: [ - { - name: "ObservableMap@4.keys()" - }, - { - name: "ObservableMap@4.temperature?" - }, - { - name: "ObservableMap@4.temperature" - }, - { - name: "ObservableMap@4.absent?" - } - ] - }) -}) - -function stripTrackerOutput(output) { - return output.map(function (i) { - if (Array.isArray(i)) return stripTrackerOutput(i) - delete i.object - delete i.time - delete i.fn - return i - }) -} - -test("spy 1", function () { - m._resetGlobalState() - const lines = [] - - const a = m.observable.box(3) - const b = m.computed(function () { - return a.get() * 2 - }) - m.autorun(function () { - b.get() - }) - const stop = m.spy(function (line) { - lines.push(line) - }) - - a.set(4) - stop() - a.set(5) - expect(stripTrackerOutput(lines)).toMatchSnapshot() -}) - -test("get atom", function () { - mobx._resetGlobalState() - mobx._getGlobalState().mobxGuid = 0 // hmm dangerous reset? - - function Clazz() { - mobx.extendObservable(this, { - a: 17 - }) - } - - const a = mobx.observable.box(3) - const b = mobx.observable({ a: 3 }) - const c = mobx.observable.map({ a: 3 }) - const d = mobx.observable([1, 2]) - const e = mobx.computed(() => 3) - const f = mobx.autorun(() => c.has("b")) - const g = new Clazz() - - function atom(thing, prop) { - return mobx.getAtom(thing, prop).constructor.name - } - - const ovClassName = mobx.observable.box(3).constructor.name - const atomClassName = mobx.createAtom("test").constructor.name - // const reactionClassName = mobx.Reaction.name - - expect(atom(a)).toBe(ovClassName) - - expect(atom(b, "a")).toBe(ovClassName) - expect(() => atom(b)).toThrow(/please specify a property/) - expect(() => atom(b, "b")).toThrow( - /no observable property 'b' found on the observable object 'ObservableObject@2'/ - ) - - expect(atom(c)).toBe(atomClassName) // returns ke, "bla".constructor, === "Atomys - expect(atom(c, "a")).toBe(ovClassName) // returns ent, "bla".constructor, === "Atomry - expect(atom(c, "b")).toBe(ovClassName) // returns has entry (see autoru, "bla", "Atomn) - expect(() => atom(c, "c")).toThrow( - /the entry 'c' does not exist in the observable map 'ObservableMap@3'/ - ) - - expect(atom(d)).toBe(atomClassName) - expect(() => atom(d, 0)).toThrow(/It is not possible to get index atoms from arrays/) - - expect(atom(e)).toBe(mobx.computed(() => {}).constructor.name) - expect(atom(f)).toBe(mobx.Reaction.name) - - expect(() => atom(g)).toThrow(/please specify a property/) - expect(atom(g, "a")).toBe(ovClassName) - - f() -}) - -test("get debug name", function () { - mobx._resetGlobalState() - mobx._getGlobalState().mobxGuid = 0 // hmm dangerous reset? - - function Clazz() { - mobx.extendObservable(this, { - a: 17 - }) - } - - const a = mobx.observable.box(3) - const b = mobx.observable({ a: 3 }) - const c = mobx.observable.map({ a: 3 }) - const d = mobx.observable([1, 2]) - const e = mobx.computed(() => 3) - const f = mobx.autorun(() => c.has("b")) - const g = new Clazz() - - function name(thing, prop) { - return mobx.getDebugName(thing, prop) - } - - expect(name(a)).toBe("ObservableValue@1") - - expect(name(b, "a")).toBe("ObservableObject@2.a") - expect(() => name(b, "b")).toThrow( - /no observable property 'b' found on the observable object 'ObservableObject@2'/ - ) - - expect(name(c)).toBe("ObservableMap@3") // returns ke, "bla"ys - expect(name(c, "a")).toBe("ObservableMap@3.a") // returns ent, "bla"ry - expect(name(c, "b")).toBe("ObservableMap@3.b?") // returns has entry (see autoru, "bla"n) - expect(() => name(c, "c")).toThrow( - /the entry 'c' does not exist in the observable map 'ObservableMap@3'/ - ) - - expect(name(d)).toBe("ObservableArray@4") - expect(() => name(d, 0)).toThrow(/It is not possible to get index atoms from arrays/) - - expect(name(e)).toBe("ComputedValue@5") - expect(name(f)).toBe("Autorun@6") - - expect(name(g)).toBe("Clazz@7") - expect(name(g, "a")).toBe("Clazz@7.a") - - f() -}) - -test("get administration", function () { - mobx._resetGlobalState() - mobx._getGlobalState().mobxGuid = 0 // hmm dangerous reset? - - function Clazz() { - mobx.extendObservable(this, { - a: 17 - }) - } - - const a = mobx.observable.box(3) - const b = mobx.observable({ a: 3 }) - const c = mobx.observable.map({ a: 3 }) - const d = mobx.observable([1, 2]) - const e = mobx.computed(() => 3) - const f = mobx.autorun(() => c.has("b")) - const g = new Clazz() - - function adm(thing, prop) { - return mobx._getAdministration(thing, prop).constructor.name - } - - const ovClassName = mobx.observable.box(3).constructor.name - const mapClassName = mobx.observable.map().constructor.name - - expect(adm(a)).toBe(ovClassName) - - expect(adm(b, "a")).toBe(ovClassName) - expect(adm(b)).toBe(b[$mobx].constructor.name) - expect(() => adm(b, "b")).toThrow( - /no observable property 'b' found on the observable object 'ObservableObject@2'/ - ) - - expect(adm(c)).toBe(mapClassName) - expect(adm(c, "a")).toBe(ovClassName) - expect(adm(c, "b")).toBe(ovClassName) - expect(() => adm(c, "c")).toThrow( - /the entry 'c' does not exist in the observable map 'ObservableMap@3'/ - ) - - expect(adm(d)).toBe(d[$mobx].constructor.name) - expect(() => adm(d, 0)).toThrow(/It is not possible to get index atoms from arrays/) - - expect(adm(e)).toBe(mobx.computed(() => {}).constructor.name) - expect(adm(f)).toBe(mobx.Reaction.name) - - expect(adm(g)).toBe(b[$mobx].constructor.name) - expect(adm(g, "a")).toBe(ovClassName) - - f() -}) - -test("onBecome(Un)Observed simple", () => { - const x = mobx.observable.box(3) - const events = [] - - mobx.onBecomeObserved(x, () => { - events.push("x observed") - }) - mobx.onBecomeUnobserved(x, () => { - events.push("x unobserved") - }) - - expect(events.length).toBe(0) // nothing happened yet - x.get() - expect(events.length).toBe(0) // nothing happened yet - x.set(4) - expect(events.length).toBe(0) // nothing happened yet - - const d5 = mobx.reaction( - () => x.get(), - () => {} - ) - expect(events.length).toBe(1) - expect(events).toEqual(["x observed"]) - - d5() - expect(events.length).toBe(2) - expect(events).toEqual(["x observed", "x unobserved"]) -}) - -test("onBecome(Un)Observed - less simple", () => { - const x = mobx.observable({ - a: 3, - get b() { - return this.a * 2 - } - }) - const events = [] - - const d1 = mobx.onBecomeObserved(x, "a", () => { - events.push("a observed") - }) - const d2 = mobx.onBecomeUnobserved(x, "a", () => { - events.push("a unobserved") - }) - const d3 = mobx.onBecomeObserved(x, "b", () => { - events.push("b observed") - }) - const d4 = mobx.onBecomeUnobserved(x, "b", () => { - events.push("b unobserved") - }) - - x.b - x.a = 4 - - expect(events.length).toBe(0) // nothing happened yet - - const d5 = mobx.reaction( - () => x.b, - () => {} - ) - expect(events.length).toBe(2) - expect(events).toEqual(["b observed", "a observed"]) - - const d6 = mobx.reaction( - () => x.b, - () => {} - ) - expect(events.length).toBe(2) - - d5() - expect(events.length).toBe(2) - d6() - expect(events.length).toBe(4) - expect(events).toEqual(["b observed", "a observed", "b unobserved", "a unobserved"]) - - d1() - d2() - d3() - d4() - events.splice(0) - const d7 = mobx.reaction( - () => x.b, - () => {} - ) - d7() - expect(events.length).toBe(0) -}) - -test("deepEquals should yield correct results for complex objects #1118 - 1", () => { - const d2016jan1 = new Date("2016-01-01") - const d2016jan1_2 = new Date("2016-01-01") - const d2017jan1 = new Date("2017-01-01") - - expect(d2016jan1).toEqual(d2016jan1_2) - expect(d2016jan1).not.toEqual(d2017jan1) - expect(mobx.comparer.structural(d2016jan1, d2016jan1)).toBe(true) - expect(mobx.comparer.structural(d2016jan1, d2017jan1)).toBe(false) - expect(mobx.comparer.structural(d2016jan1, d2016jan1_2)).toBe(true) -}) - -test("deepEquals should yield correct results for complex objects #1118 - 2", () => { - class A { - x = 3 - y = 4 - - constructor(x) { - this.x = x - } - } - - const a1 = new A(2) - const a2 = new A(2) - const a3 = new A(3) - const a4 = new A(2) - a4.z = 2 - - expect(a1).toEqual(a2) - expect(a1).not.toEqual(a3) - expect(mobx.comparer.structural(a1, a1)).toBe(true) - expect(mobx.comparer.structural(a1, a3)).toBe(false) - expect(mobx.comparer.structural(a1, a2)).toBe(true) - expect(mobx.comparer.structural(a1, a4)).toBe(false) -}) - -test("comparer.shallow should work", () => { - const sh = mobx.comparer.shallow - - expect(sh(1, 1)).toBe(true) - - expect(sh(1, 2)).toBe(false) - - expect(sh({}, {})).toBe(true) - expect(sh([], [])).toBe(true) - - expect(sh({}, [])).toBe(false) - expect(sh([], {})).toBe(false) - - expect(sh({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 })).toBe(true) - - expect(sh({ a: 1, b: 2, c: 3, d: 4 }, { a: 1, b: 2, c: 3 })).toBe(false) - expect(sh({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3, d: 4 })).toBe(false) - expect(sh({ a: {}, b: 2, c: 3 }, { a: {}, b: 2, c: 3 })).toBe(false) - - expect(sh([1, 2, 3], [1, 2, 3])).toBe(true) - - expect(sh([1, 2, 3, 4], [1, 2, 3])).toBe(false) - expect(sh([1, 2, 3], [1, 2, 3, 4])).toBe(false) - expect(sh([{}, 2, 3], [{}, 2, 3])).toBe(false) -}) diff --git a/packages/mobx/__tests__/v4/base/intercept.js b/packages/mobx/__tests__/v4/base/intercept.js deleted file mode 100644 index 1e17612b87..0000000000 --- a/packages/mobx/__tests__/v4/base/intercept.js +++ /dev/null @@ -1,189 +0,0 @@ -const m = require("../mobx4") -const intercept = m.intercept - -test("intercept observable value", () => { - const a = m.observable.box(1) - - let d = intercept(a, () => { - return null - }) - - a.set(2) - - expect(a.get()).toBe(1) - - d() - - a.set(3) - expect(a.get()).toBe(3) - - d = intercept(a, c => { - if (c.newValue % 2 === 0) { - throw "value should be odd!" - } - return c - }) - - expect(() => { - a.set(4) - }).toThrow(/value should be odd/) - - expect(a.get()).toBe(3) - a.set(5) - expect(a.get()).toBe(5) - - d() - d = intercept(a, c => { - c.newValue *= 2 - return c - }) - - a.set(6) - expect(a.get()).toBe(12) - - intercept(a, c => { - c.newValue += 1 - return c - }) - - a.set(7) - expect(a.get()).toBe(15) - - d() - a.set(8) - expect(a.get()).toBe(9) -}) - -test("intercept array", () => { - const a = m.observable([1, 2]) - - let d = m.intercept(a, () => null) - a.push(2) - expect(a.slice()).toEqual([1, 2]) - - d() - - d = intercept(a, c => { - if (c.type === "splice") { - c.added.push(c.added[0] * 2) - c.removedCount = 1 - return c - } else if (c.type === "update") { - c.newValue = c.newValue * 3 - return c - } - }) - - a.unshift(3, 4) - - expect(a.slice()).toEqual([3, 4, 6, 2]) - a[2] = 5 - expect(a.slice()).toEqual([3, 4, 15, 2]) -}) - -test("intercept object", () => { - const a = m.observable({ - b: 3 - }) - - intercept(a, change => { - change.newValue *= 3 - return change - }) - - a.b = 4 - - expect(a.b).toBe(12) - - intercept(a, "b", change => { - change.newValue += 1 - return change - }) - - a.b = 5 - expect(a.b).toBe(16) - - const d3 = intercept(a, c => { - expect(c.name).toBe("b") - expect(c.object).toBe(a) - expect(c.type).toBe("update") - return null - }) - - a.b = 7 - expect(a.b).toBe(16) - - d3() - a.b = 7 - expect(a.b).toBe(22) -}) - -test("intercept property additions", () => { - const a = m.observable({}) - const d4 = intercept(a, change => { - if (change.type === "add") { - return null - } - return change - }) - - m.extendObservable(a, { c: 1 }) // not added! - expect(a.c).toBe(undefined) - expect(m.isObservableProp(a, "c")).toBe(false) - - d4() - - m.extendObservable(a, { c: 2 }) - expect(a.c).toBe(2) - expect(m.isObservableProp(a, "c")).toBe(true) -}) - -test("intercept map", () => { - const a = m.observable.map({ - b: 3 - }) - - intercept(a, c => { - c.newValue *= 3 - return c - }) - - a.set("b", 4) - - expect(a.get("b")).toBe(12) - - intercept(a, "b", c => { - c.newValue += 1 - return c - }) - - a.set("b", 5) - expect(a.get("b")).toBe(16) - - const d3 = intercept(a, c => { - expect(c.name).toBe("b"), expect(c.object).toBe(a) - expect(c.type).toBe("update") - return null - }) - - a.set("b", 7) - expect(a.get("b")).toBe(16) - - d3() - a.set("b", 7) - expect(a.get("b")).toBe(22) - - const d4 = intercept(a, c => { - if (c.type === "delete") return null - return c - }) - - a.delete("b") - expect(a.has("b")).toBe(true) - expect(a.get("b")).toBe(22) - - d4() - a.delete("b") - expect(a.has("b")).toBe(false) - expect(a.get("c")).toBe(undefined) -}) diff --git a/packages/mobx/__tests__/v4/base/jsconfig.json b/packages/mobx/__tests__/v4/base/jsconfig.json deleted file mode 100644 index 78e52b12d1..0000000000 --- a/packages/mobx/__tests__/v4/base/jsconfig.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "compilerOptions": { - "target": "es2015", - "experimentalDecorators": true - } -} diff --git a/packages/mobx/__tests__/v4/base/makereactive.js b/packages/mobx/__tests__/v4/base/makereactive.js deleted file mode 100644 index c7ebbb3d9d..0000000000 --- a/packages/mobx/__tests__/v4/base/makereactive.js +++ /dev/null @@ -1,642 +0,0 @@ -const mobx = require("../mobx4") -const m = mobx -const o = mobx.observable -const { makeObservable } = mobx - -function buffer() { - const b = [] - const res = function (x) { - b.push(x) - } - res.toArray = function () { - return b - } - return res -} - -test("isObservable", function () { - function Order() {} - - function ReactiveOrder(price) { - m.extendObservable(this, { - price: price - }) - } - expect(m.isObservable(null)).toBe(false) - expect(m.isObservable(null)).toBe(false) - - expect(m.isObservable(m.observable([]))).toBe(true) - expect(m.isObservable(m.observable({}))).toBe(true) - expect(m.isObservable(m.observable.box(function () {}))).toBe(true) - expect(m.isObservable(m.computed(function () {}))).toBe(true) - - expect(m.isObservable([])).toBe(false) - expect(m.isObservable({})).toBe(false) - expect(m.isObservable(function () {})).toBe(false) - - expect(m.isObservable(new Order())).toBe(false) - expect(m.isObservable(m.observable.box(new Order()))).toBe(true) - - expect(m.isObservable(new ReactiveOrder())).toBe(true) - expect(m.isObservable(m.observable.box(3))).toBe(true) - - const obj = {} - expect(m.isObservable(obj)).toBe(false) - - expect(m.isObservable(m.observable.box(function () {}))).toBe(true) - expect(m.isObservable(m.autorun(function () {}))).toBe(true) - - expect(m.isObservableProp(m.observable({ a: 1 }), "a")).toBe(true) - expect(m.isObservableProp(m.observable({ a: 1 }), "b")).toBe(false) - - expect(m.isObservable(m.observable.map())).toBe(true) - - const base = { a: 3 } - const obs = m.observable(base) - expect(m.isObservable(base)).toBe(false) - expect(m.isObservableProp(base, "a")).toBe(false) - expect(m.isObservable(obs)).toBe(true) - expect(m.isObservableProp(obs, "a")).toBe(true) -}) - -test("isBoxedObservable", function () { - expect(m.isBoxedObservable(m.observable({}))).toBe(false) - expect(m.isBoxedObservable(m.computed(() => 3))).toBe(false) - expect(m.isBoxedObservable(m.observable.box(3))).toBe(true) - expect(m.isBoxedObservable(m.observable.box(3))).toBe(true) - expect(m.isBoxedObservable(m.observable.box({}))).toBe(true) -}) - -test("observable1", function () { - m._resetGlobalState() - - // recursive structure - const x = m.observable({ - a: { - b: { - c: 3 - } - } - }) - const b = buffer() - m.autorun(function () { - b(x.a.b.c) - }) - x.a = { b: { c: 4 } } - x.a.b.c = 5 // new structure was reactive as well - expect(b.toArray()).toEqual([3, 4, 5]) - - // recursive structure, but asReference passed in - expect(m.isObservable(x.a.b)).toBe(true) - const x2 = m.observable.object( - { - a: { - b: { - c: 3 - } - } - }, - { - a: m.observable.ref - } - ) - - expect(m.isObservable(x2)).toBe(true) - expect(m.isObservable(x2.a)).toBe(false) - expect(m.isObservable(x2.a.b)).toBe(false) - - const b2 = buffer() - m.autorun(function () { - b2(x2.a.b.c) - }) - x2.a = { b: { c: 4 } } - x2.a.b.c = 5 // not picked up, not reactive, since passed as reference - expect(b2.toArray()).toEqual([3, 4]) - - // non recursive structure - const x3 = o.object( - { - a: { - b: { - c: 3 - } - } - }, - {}, - { deep: false } - ) - const b3 = buffer() - m.autorun(function () { - b3(x3.a.b.c) - }) - x3.a = { b: { c: 4 } } - x3.a.b.c = 5 // sub structure not reactive - expect(b3.toArray()).toEqual([3, 4]) -}) - -test("observable3", function () { - function Order(price) { - this.price = price - } - - const x = m.observable({ - orders: [new Order(1), new Order(2)] - }) - - const b = buffer() - m.autorun(function () { - b(x.orders.length) - }) - - expect(m.isObservable(x.orders)).toBe(true) - expect(m.isObservable(x.orders[0])).toBe(false) - x.orders[2] = new Order(3) - x.orders = [] - expect(m.isObservable(x.orders)).toBe(true) - x.orders[0] = new Order(2) - expect(b.toArray()).toEqual([2, 3, 0, 1]) -}) - -test("observable4", function () { - const x = m.observable([{ x: 1 }, { x: 2 }]) - - const b = buffer() - m.observe( - m.computed(function () { - return x.map(function (d) { - return d.x - }) - }), - x => b(x.newValue), - true - ) - - x[0].x = 3 - x.shift() - x.push({ x: 5 }) - expect(b.toArray()).toEqual([[1, 2], [3, 2], [2], [2, 5]]) - - // non recursive - const x2 = o.array([{ x: 1 }, { x: 2 }], { deep: false }) - - const b2 = buffer() - m.observe( - m.computed(function () { - return x2.map(function (d) { - return d.x - }) - }), - x => b2(x.newValue), - true - ) - - x2[0].x = 3 - x2.shift() - x2.push({ x: 5 }) - expect(b2.toArray()).toEqual([[1, 2], [2], [2, 5]]) -}) - -test("observable5", function () { - let x = m.computed(function () {}) - expect(function () { - x.set(7) // set not allowed - }).toThrow(/It is not possible to assign a new value to a computed value/) - - let f = m._autoAction(function () {}) - const x2 = m.observable.box(f) - expect(x2.get()).toBe(f) - x2.set(null) // allowed - - f = m._autoAction(function () { - return this.price - }) - x = m.observable( - { - price: 17, - get reactive() { - return this.price - }, - nonReactive: f - }, - { - nonReactive: false - } - ) - - const b = buffer() - m.autorun(function () { - b([x.reactive, x.nonReactive, x.nonReactive()]) - }) - - x.price = 18 - const three = function () { - return 3 - } - x.nonReactive = three - - expect(b.toArray()).toEqual([ - [17, f, 17], - [18, f, 18] - ]) -}) - -test("flat array", function () { - const x = m.observable.object( - { - x: [ - { - a: 1 - } - ] - }, - { x: m.observable.shallow } - ) - - let result - let updates = 0 - m.autorun(function () { - updates++ - result = JSON.stringify(mobx.toJS(x)) - }) - - expect(result).toEqual(JSON.stringify({ x: [{ a: 1 }] })) - expect(updates).toBe(1) - - x.x[0].a = 2 // not picked up; object is not made reactive - expect(result).toEqual(JSON.stringify({ x: [{ a: 1 }] })) - expect(updates).toBe(1) - - x.x.push({ a: 3 }) // picked up, array is reactive - expect(result).toEqual(JSON.stringify({ x: [{ a: 2 }, { a: 3 }] })) - expect(updates).toBe(2) - - x.x[0] = { a: 4 } // picked up, array is reactive - expect(result).toEqual(JSON.stringify({ x: [{ a: 4 }, { a: 3 }] })) - expect(updates).toBe(3) - - x.x[1].a = 6 // not picked up - expect(result).toEqual(JSON.stringify({ x: [{ a: 4 }, { a: 3 }] })) - expect(updates).toBe(3) -}) - -test("flat object", function () { - const y = m.observable.object( - { - x: { z: 3 } - }, - {}, - { deep: false } - ) - - let result - let updates = 0 - m.autorun(function () { - updates++ - result = JSON.stringify(mobx.toJS(y)) - }) - - expect(result).toEqual(JSON.stringify({ x: { z: 3 } })) - expect(updates).toBe(1) - - y.x.z = 4 // not picked up - expect(result).toEqual(JSON.stringify({ x: { z: 3 } })) - expect(updates).toBe(1) - - y.x = { z: 5 } - expect(result).toEqual(JSON.stringify({ x: { z: 5 } })) - expect(updates).toBe(2) - - y.x.z = 6 // not picked up - expect(result).toEqual(JSON.stringify({ x: { z: 5 } })) - expect(updates).toBe(2) -}) - -test("as structure", function () { - const x = m.observable.object( - { - x: null - }, - { - x: m.observable.struct - } - ) - - let changed = 0 - const dis = m.autorun(function () { - changed++ - JSON.stringify(x) - }) - - function c() { - expect(changed).toBe(1) - if (changed !== 1) console.trace() - changed = 0 - } - - function nc() { - expect(changed).toBe(0) - if (changed !== 0) console.trace() - changed = 0 - } - - // nc = no change, c = changed. - c() - x.x = null - nc() - x.x = undefined - c() - x.x = 3 - c() - x.x = 1 * x.x - nc() - x.x = "3" - c() - - x.x = { - y: 3 - } - c() - x.x.y = 3 - nc() - x.x = { - y: 3 - } - nc() - x.x = { - y: 4 - } - c() - x.x = { - y: 3 - } - c() - x.x = { - y: { - y: 3 - } - } - c() - x.x.y.y = 3 - nc() - x.x.y = { y: 3 } - nc() - x.x = { y: { y: 3 } } - nc() - x.x = { y: { y: 4 } } - c() - x.x = {} - c() - x.x = {} - nc() - - x.x = [] - c() - x.x = [] - nc() - x.x = [3, 2, 1] - c() - x.x.sort() - nc() - x.x.sort() - nc() - x.x[1] = 2 - nc() - x.x[0] = 0 - nc() // not detected - - dis() -}) - -test("as structure view", function () { - const x = m.observable.object( - { - a: 1, - aa: 1, - get b() { - this.a - return { a: this.aa } - }, - get c() { - this.b - return { a: this.aa } - } - }, - { - c: m.computed({ compareStructural: true }) - } - ) - - let bc = 0 - m.autorun(function () { - x.b - bc++ - }) - expect(bc).toBe(1) - - let cc = 0 - m.autorun(function () { - x.c - cc++ - }) - expect(cc).toBe(1) - - x.a = 2 - x.a = 3 - expect(bc).toBe(3) - expect(cc).toBe(1) - x.aa = 3 - expect(bc).toBe(4) - expect(cc).toBe(2) -}) - -test("540 - extendobservable should not report cycles", function () { - expect(() => m.extendObservable(Object.freeze({}), {})).toThrow( - /Cannot make the designated object observable/ - ) - - const objWrapper = mobx.observable({ - value: null - }) - - const obj = { - name: "Hello" - } - - objWrapper.value = obj - expect(() => mobx.extendObservable(objWrapper, objWrapper.value)).toThrow( - /Extending an object with another observable \(object\) is not supported/ - ) -}) - -test("computed value", () => { - mobx._getGlobalState().mobxGuid = 0 - const c = mobx.computed(() => 3) - - expect(0 + c).toBe(3) - expect(mobx.isComputed(c)).toBe(true) - expect(c.toString()).toMatchSnapshot() -}) - -test("boxed value json", () => { - const a = mobx.observable.box({ x: 1 }) - expect(a.get().x).toEqual(1) - a.set(3) - expect(a.get()).toEqual(3) - expect("" + a).toBe("3") - expect(a.toJSON()).toBe(3) -}) - -test("computed value scope", () => { - const a = mobx.observable({ - x: 1, - get y() { - return this.x * 2 - }, - set y(v) { - this.x = v - } - }) - - expect(a.y).toBe(2) - a.x = 2 - expect(a.y).toBe(4) - a.y = 3 - expect(a.y).toBe(6) -}) - -test("shallow array", () => { - const a = mobx.observable.array([], { deep: false }) - a.push({ x: 1 }, [], 2, mobx.observable({ y: 3 })) - - expect(mobx.isObservable(a)).toBe(true) - expect(mobx.isObservable(a[0])).toBe(false) - expect(mobx.isObservable(a[1])).toBe(false) - expect(mobx.isObservable(a[2])).toBe(false) - expect(mobx.isObservable(a[3])).toBe(true) -}) - -test("761 - deeply nested modifiers work", () => { - const a = {} - mobx.extendObservable(a, { - someKey: mobx.observable.object( - { - someNestedKey: [] - }, - { - someNestedKey: mobx.observable.ref - } - ) - }) - - expect(mobx.isObservable(a)).toBe(true) - expect(mobx.isObservableProp(a, "someKey")).toBe(true) - expect(mobx.isObservable(a.someKey)).toBe(true) - expect(mobx.isObservableProp(a.someKey, "someNestedKey")).toBe(true) - expect(mobx.isObservable(a.someKey.someNestedKey)).toBe(false) - expect(Array.isArray(a.someKey.someNestedKey)).toBe(true) - - Object.assign(a, { someKey: { someNestedKey: [1, 2, 3] } }) - expect(mobx.isObservable(a)).toBe(true) - expect(mobx.isObservableProp(a, "someKey")).toBe(true) - expect(mobx.isObservable(a.someKey)).toBe(true) - expect(mobx.isObservableProp(a.someKey, "someNestedKey")).toBe(true) - expect(mobx.isObservable(a.someKey.someNestedKey)).toBe(true) // Too bad: no deep merge with Object.assign! someKey object gets replaced in its entirity - expect(Array.isArray(a.someKey.someNestedKey)).toBe(false) -}) - -test("compare structurally, ref", () => { - const a = mobx.observable.object( - { - x: undefined - }, - { - x: mobx.observable.struct - } - ) - - let changed = 0 - const d = mobx.autorun(() => { - mobx.toJS(a) - changed++ - }) - - expect(changed).toBe(1) - a.x = { y: 2 } - expect(changed).toBe(2) - a.x.y = 3 - expect(mobx.isObservable(a.x)).toBe(false) - expect(changed).toBe(2) - - a.x = { y: 3 } - expect(changed).toBe(2) - - a.x = { y: 4 } - expect(changed).toBe(3) - a.x = { y: 4 } - expect(changed).toBe(3) - - d() -}) - -test("structural collections", () => { - const o = mobx.observable( - { - x: [1, 2, 3] - }, - { - x: mobx.observable.struct - } - ) - - expect(mobx.isObservable(o.x)).toBeFalsy() - const x = o.x - o.x = [1, 2, 3] - expect(o.x).toBe(x) - expect(() => { - o.x = mobx.observable([1, 2, 3]) - }).toThrow("observable.struct should not be used with observable values") -}) - -test("yest object equals issue - reference", () => { - class Store { - constructor() { - mobx.extendObservable(this, { x: 3 }) - } - } - - const store = new Store() - expect(store).toEqual(new Store()) -}) - -test("yest object equals issue", () => { - class Store { - @mobx.observable x = 2 - - constructor() { - makeObservable(this) - this.x = 3 - } - } - - const store = new Store() - expect(store).toEqual(new Store()) -}) - -test("yest array equals issue", () => { - class Store { - @mobx.observable things = [] - - constructor() { - makeObservable(this) - } - } - - const store = new Store() - expect(store.things).toEqual([]) -}) - -test("#1650, toString is not treated correctly", () => { - const o = { a: "a", toString: "toString" } - const oo = mobx.observable(o) - expect(oo.toString).toBe("toString") -}) diff --git a/packages/mobx/__tests__/v4/base/object-api.js b/packages/mobx/__tests__/v4/base/object-api.js deleted file mode 100644 index 911658df4f..0000000000 --- a/packages/mobx/__tests__/v4/base/object-api.js +++ /dev/null @@ -1,538 +0,0 @@ -const mobx = require("../mobx4") -const { autorun, keys, when, set, remove, values, entries, reaction, observable, has, get } = mobx - -test("keys should be observable when extending", () => { - const todos = observable({}) - - const todoTitles = [] - reaction( - () => keys(todos).map(key => `${key}: ${todos[key]}`), - titles => todoTitles.push(titles.join(",")) - ) - - mobx.set(todos, { - lewis: "Read Lewis", - chesterton: "Be mind blown by Chesterton" - }) - expect(todoTitles).toEqual(["lewis: Read Lewis,chesterton: Be mind blown by Chesterton"]) - - mobx.set(todos, { lewis: "Read Lewis twice" }) - mobx.set(todos, { coffee: "Grab coffee" }) - expect(todoTitles).toEqual([ - "lewis: Read Lewis,chesterton: Be mind blown by Chesterton", - "lewis: Read Lewis twice,chesterton: Be mind blown by Chesterton", - "lewis: Read Lewis twice,chesterton: Be mind blown by Chesterton,coffee: Grab coffee" - ]) -}) - -test("toJS respects key changes", () => { - const todos = observable({}) - - const serialized = [] - mobx.autorun(() => { - serialized.push(JSON.stringify(mobx.toJS(todos))) - }) - - mobx.set(todos, { - lewis: "Read Lewis", - chesterton: "Be mind blown by Chesterton" - }) - mobx.set(todos, { lewis: "Read Lewis twice" }) - mobx.set(todos, { coffee: "Grab coffee" }) - expect(serialized).toEqual([ - "{}", - '{"lewis":"Read Lewis","chesterton":"Be mind blown by Chesterton"}', - '{"lewis":"Read Lewis twice","chesterton":"Be mind blown by Chesterton"}', - '{"lewis":"Read Lewis twice","chesterton":"Be mind blown by Chesterton","coffee":"Grab coffee"}' - ]) -}) - -test("keys(object), values(object), entries(object)", () => { - const todos = observable({}) - const plain = {} - const keysSnapshots = [] - const valuesSnapshots = [] - const entriesSnapshots = [] - const expectedKeysSnapshots = [] - const expectedValuesSnapshots = [] - const expectedEntriesSnapshots = [] - - const s1 = Symbol() - const s2 = Symbol() - - function expectEquality() { - expect(todos).toEqual(plain) - } - - function expectKeysReaction() { - expectedKeysSnapshots.push(Object.keys(plain)) - } - - function expectValuesReaction() { - expectedValuesSnapshots.push(Object.values(plain)) - } - - function expectEntriesReaction() { - expectedEntriesSnapshots.push(Object.entries(plain)) - } - - reaction( - () => keys(todos), - result => keysSnapshots.push(result) - ) - - reaction( - () => values(todos), - result => valuesSnapshots.push(result) - ) - - reaction( - () => entries(todos), - result => entriesSnapshots.push(result) - ) - - expectEquality() - // add - set(todos, "k1", 1) - plain["k1"] = 1 - expectEquality() - expectKeysReaction() - expectValuesReaction() - expectEntriesReaction() - // add symbol - set(todos, s1, 2) - plain[s1] = 2 - expectEquality() - // see ObservableObjectAdministration.keys() for explanation - expectKeysReaction() - expectValuesReaction() - expectEntriesReaction() - // delete non-existent - remove(todos, "-") - delete plain["-"] - expectEquality() - // delete non-existent symbol - remove(todos, Symbol()) - delete plain[Symbol()] - expectEquality() - // add second - set(todos, "k2", 3) - plain["k2"] = 3 - expectEquality() - expectKeysReaction() - expectValuesReaction() - expectEntriesReaction() - // add second symbol - set(todos, s2, 4) - plain[s2] = 4 - expectEquality() - // see ObservableObjectAdministration.keys() for explanation - expectKeysReaction() - expectValuesReaction() - expectEntriesReaction() - // update - set(todos, "k1", 11) - plain["k1"] = 11 - expectEquality() - expectValuesReaction() - expectEntriesReaction() - // update symbol - set(todos, s1, 22) - plain[s1] = 22 - expectEquality() - // delete - remove(todos, "k1") - delete plain["k1"] - expectEquality() - expectKeysReaction() - expectValuesReaction() - expectEntriesReaction() - // delete symbol - remove(todos, s1) - delete plain[s1] - expectEquality() - // see ObservableObjectAdministration.keys() for explanation - expectKeysReaction() - expectValuesReaction() - expectEntriesReaction() - - expect(keysSnapshots).toEqual(expectedKeysSnapshots) - expect(valuesSnapshots).toEqual(expectedValuesSnapshots) - expect(entriesSnapshots).toEqual(expectedEntriesSnapshots) -}) - -test("map - set, remove, values are reactive", () => { - const todos = observable.map({}) - const snapshots = [] - - reaction( - () => values(todos), - values => snapshots.push(values) - ) - - expect(has(todos, "x")).toBe(false) - expect(get(todos, "x")).toBe(undefined) - set(todos, "x", 3) - expect(has(todos, "x")).toBe(true) - expect(get(todos, "x")).toBe(3) - remove(todos, "y") - set(todos, "z", 4) - set(todos, "x", 5) - remove(todos, "z") - - expect(snapshots).toEqual([[3], [3, 4], [5, 4], [5]]) -}) - -test("map - set, remove, entries are reactive", () => { - const todos = observable.map({}) - const snapshots = [] - - reaction( - () => entries(todos), - entries => snapshots.push(entries) - ) - - expect(has(todos, "x")).toBe(false) - expect(get(todos, "x")).toBe(undefined) - set(todos, "x", 3) - expect(has(todos, "x")).toBe(true) - expect(get(todos, "x")).toBe(3) - remove(todos, "y") - set(todos, "z", 4) - set(todos, "x", 5) - remove(todos, "z") - - expect(snapshots).toEqual([ - [["x", 3]], - [ - ["x", 3], - ["z", 4] - ], - [ - ["x", 5], - ["z", 4] - ], - [["x", 5]] - ]) -}) - -test("map - set, remove, keys are reactive", () => { - const todos = observable.map({ a: 3 }) - const snapshots = [] - - reaction( - () => keys(todos), - keys => snapshots.push(keys) - ) - - set(todos, "x", 3) - remove(todos, "y") - set(todos, "z", 4) - set(todos, "x", 5) - remove(todos, "z") - remove(todos, "a") - - expect(snapshots).toEqual([["a", "x"], ["a", "x", "z"], ["a", "x"], ["x"]]) -}) - -test("array - set, remove, values are reactive", () => { - const todos = observable.array() - const snapshots = [] - - reaction( - () => values(todos), - values => snapshots.push(values) - ) - - expect(has(todos, 0)).toBe(false) - expect(get(todos, 0)).toBe(undefined) - set(todos, 0, 2) - expect(has(todos, 0)).toBe(true) - expect(get(todos, 0)).toBe(2) - - set(todos, "1", 4) - set(todos, 3, 4) - set(todos, 1, 3) - remove(todos, 2) - remove(todos, "0") - - expect(snapshots).toEqual([ - [2], - [2, 4], - [2, 4, undefined, 4], - [2, 3, undefined, 4], - [2, 3, 4], - [3, 4] - ]) -}) - -test("array - set, remove, entries are reactive", () => { - const todos = observable.array() - const snapshots = [] - - reaction( - () => entries(todos), - entries => snapshots.push(entries) - ) - - expect(has(todos, 0)).toBe(false) - expect(get(todos, 0)).toBe(undefined) - set(todos, 0, 2) - expect(has(todos, 0)).toBe(true) - expect(get(todos, 0)).toBe(2) - - set(todos, "1", 4) - set(todos, 3, 4) - set(todos, 1, 3) - remove(todos, 2) - remove(todos, "0") - - expect(snapshots).toEqual([ - [[0, 2]], - [ - [0, 2], - [1, 4] - ], - [ - [0, 2], - [1, 4], - [2, undefined], - [3, 4] - ], - [ - [0, 2], - [1, 3], - [2, undefined], - [3, 4] - ], - [ - [0, 2], - [1, 3], - [2, 4] - ], - [ - [0, 3], - [1, 4] - ] - ]) -}) - -test("array - set, remove, keys are reactive", () => { - const todos = observable.array() - const snapshots = [] - - reaction( - () => keys(todos), - keys => snapshots.push(keys) - ) - - set(todos, 0, 2) - set(todos, "1", 4) - set(todos, 3, 4) - set(todos, 1, 3) - remove(todos, 2) - remove(todos, "0") - - expect(snapshots).toEqual([[0], [0, 1], [0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2], [0, 1]]) -}) - -test("observe & intercept", () => { - let events = [] - const todos = observable( - { - a: { title: "get coffee" } - }, - {}, - { deep: false, name: "TestObject" } - ) - mobx.observe(todos, c => events.push({ observe: c })) - const d = mobx.intercept(todos, c => { - events.push({ intercept: c }) - return null // no addition! - }) - - set(todos, { b: { title: "get tea" } }) - remove(todos, "a") - expect(events).toMatchSnapshot() - expect(mobx.toJS(todos)).toEqual({ - a: { title: "get coffee" } - }) - - events.splice(0) - d() - set(todos, { b: { title: "get tea" } }) - remove(todos, "a") - expect(events).toMatchSnapshot() - expect(mobx.toJS(todos)).toEqual({ - b: { title: "get tea" } - }) -}) - -test("observe & intercept set called multiple times", () => { - const a = mobx.observable({}) - const interceptLogs = [] - const observeLogs = [] - - mobx.intercept(a, change => { - interceptLogs.push(`${change.name}: ${change.newValue}`) - return change - }) - mobx.observe(a, change => observeLogs.push(`${change.name}: ${change.newValue}`)) - - mobx.set(a, "x", 0) - a.x = 1 - mobx.set(a, "x", 2) - - expect(interceptLogs).toEqual(["x: 0", "x: 1", "x: 2"]) - expect(observeLogs).toEqual(["x: 0", "x: 1", "x: 2"]) -}) - -test("dynamically adding properties should preserve the original modifiers of an object", () => { - const todos = observable( - { - a: { title: "get coffee" } - }, - {}, - { deep: false } - ) - expect(mobx.isObservable(todos.a)).toBe(false) - set(todos, { b: { title: "get tea" } }) - expect(mobx.isObservable(todos.b)).toBe(false) -}) - -test("has and get are reactive", async () => { - const todos = observable({}) - - const p1 = when(() => has(todos, "x")) - const p2 = when(() => get(todos, "y") === 3) - - setTimeout(() => { - set(todos, { x: false, y: 3 }) - }, 100) - - await p1 - await p2 -}) - -test("computed props are considered part of collections", () => { - const x = observable({ - get y() { - return 3 - } - }) - expect(mobx.isComputedProp(x, "y")).toBe(true) - expect(x.y).toBe(3) - expect(has(x, "y")).toBe(true) - expect(get(x, "y")).toBe(3) - expect(keys(x)).toEqual([]) - expect(values(x)).toEqual([]) - expect(entries(x)).toEqual([]) -}) - -test("#1739 - delete and undelete should work", () => { - const x = observable({}) - - const events = [] - autorun(() => { - events.push(has(x, "a")) - }) - - set(x, "a", 1) - set(x, "a", 2) - remove(x, "a") - set(x, "a", 2) - remove(x, "a") - set(x, "a", 3) - expect(events).toEqual([false, true, false, true, false, true]) -}) - -test("set - set, remove, keys are reactive", () => { - const todos = observable.set([1]) - const snapshots = [] - - reaction( - () => keys(todos), - keys => snapshots.push(keys) - ) - - set(todos, 2) - remove(todos, 2) - set(todos, 3) - set(todos, 4) - remove(todos, 3) - - expect(snapshots).toEqual([[1, 2], [1], [1, 3], [1, 3, 4], [1, 4]]) -}) - -test("defineProperty - configurable: false", () => { - const obj = mobx.observable({}) - const desc = { - enumerable: true, - configurable: false, - writable: true, - value: 0 - } - mobx.defineProperty(obj, "foo", desc) - expect(Object.getOwnPropertyDescriptor(obj, "foo")).toEqual(desc) - expect(mobx.isObservableProp(obj, "foo")).toBe(false) - obj.foo++ - expect(obj.foo).toBe(1) - expect(() => mobx.extendObservable(obj, { foo: 0 })).toThrow(TypeError) - expect(() => mobx.makeObservable(obj, { foo: mobx.observable })).toThrow(TypeError) - expect(() => mobx.defineProperty(obj, "foo", { configurable: false })).toThrow(TypeError) -}) - -test("defineProperty - writable: false", () => { - const obj = mobx.observable({}) - const desc = { - enumerable: true, - configurable: true, - writable: false, - value: 0 - } - mobx.defineProperty(obj, "foo", desc) - expect(Object.getOwnPropertyDescriptor(obj, "foo")).toEqual(desc) - expect(mobx.isObservableProp(obj, "foo")).toBe(false) - expect(() => obj.foo++).toThrow(TypeError) - mobx.extendObservable(obj, { foo: 0 }) - expect(mobx.isObservableProp(obj, "foo")).toBe(true) - obj.foo++ - expect(obj.foo).toBe(1) -}) - -test("defineProperty - redefine observable", () => { - const obj = mobx.observable({ foo: 0 }) - expect(mobx.isObservableProp(obj, "foo")).toBe(true) - const desc = { - enumerable: true, - configurable: true, - writable: false, - value: 0 - } - mobx.defineProperty(obj, "foo", desc) - expect(Object.getOwnPropertyDescriptor(obj, "foo")).toEqual(desc) - expect(mobx.isObservableProp(obj, "foo")).toBe(false) -}) - -test("defineProperty notifies keys observers", () => { - const obj = mobx.observable({}) - let reactionCount = 0 - reaction( - () => mobx.keys(obj), - () => reactionCount++ - ) - - const desc = { - enumerable: true, - configurable: true, - writable: true, - value: 0 - } - mobx.defineProperty(obj, "foo", desc) - expect(Object.getOwnPropertyDescriptor(obj, "foo")).toEqual(desc) - expect(mobx.isObservableProp(obj, "foo")).toBe(false) - expect(reactionCount).toBe(1) - mobx.remove(obj, 'foo') - expect(obj.hasOwnProperty("foo")).toBe(false) - expect(reactionCount).toBe(2) -}) \ No newline at end of file diff --git a/packages/mobx/__tests__/v4/base/observables.js b/packages/mobx/__tests__/v4/base/observables.js deleted file mode 100644 index a67f07e14d..0000000000 --- a/packages/mobx/__tests__/v4/base/observables.js +++ /dev/null @@ -1,2022 +0,0 @@ -"use strict" - -const mobx = require("../mobx4") -const m = mobx -const { $mobx, makeObservable, observable, computed, transaction, autorun, extendObservable } = mobx -const utils = require("../../v5/utils/test-utils") - -const voidObserver = function () {} - -function buffer() { - const b = [] - const res = function (x) { - if (typeof x.newValue === "object") { - const copy = { ...x.newValue } - delete copy[$mobx] - b.push(copy) - } else { - b.push(x.newValue) - } - } - res.toArray = function () { - return b - } - return res -} - -test("argumentless observable", () => { - const a = observable.box() - - expect(m.isObservable(a)).toBe(true) - expect(a.get()).toBe(undefined) -}) - -test("basic", function () { - const x = observable.box(3) - const b = buffer() - m.observe(x, b) - expect(3).toBe(x.get()) - - x.set(5) - expect(5).toBe(x.get()) - expect([5]).toEqual(b.toArray()) - expect(mobx._isComputingDerivation()).toBe(false) -}) - -test("basic2", function () { - const x = observable.box(3) - const z = computed(function () { - return x.get() * 2 - }) - const y = computed(function () { - return x.get() * 3 - }) - - m.observe(z, voidObserver) - - expect(z.get()).toBe(6) - expect(y.get()).toBe(9) - - x.set(5) - expect(z.get()).toBe(10) - expect(y.get()).toBe(15) - - expect(mobx._isComputingDerivation()).toBe(false) -}) - -test("computed with asStructure modifier", function () { - const x1 = observable.box(3) - const x2 = observable.box(5) - const y = m.computed( - function () { - return { - sum: x1.get() + x2.get() - } - }, - { compareStructural: true } - ) - const b = buffer() - m.observe(y, b, true) - - expect(8).toBe(y.get().sum) - - x1.set(4) - expect(9).toBe(y.get().sum) - - m.transaction(function () { - // swap values, computation results is structuraly unchanged - x1.set(5) - x2.set(4) - }) - - expect(b.toArray()).toEqual([{ sum: 8 }, { sum: 9 }]) - expect(mobx._isComputingDerivation()).toBe(false) -}) - -test("dynamic", function (done) { - try { - const x = observable.box(3) - const y = m.computed(function () { - return x.get() - }) - const b = buffer() - m.observe(y, b, true) - - expect(3).toBe(y.get()) // First evaluation here.. - - x.set(5) - expect(5).toBe(y.get()) - - expect(b.toArray()).toEqual([3, 5]) - expect(mobx._isComputingDerivation()).toBe(false) - - done() - } catch (e) { - console.log(e.stack) - } -}) - -test("dynamic2", function (done) { - try { - const x = observable.box(3) - const y = computed(function () { - return x.get() * x.get() - }) - - expect(9).toBe(y.get()) - const b = buffer() - m.observe(y, b) - - x.set(5) - expect(25).toBe(y.get()) - - //no intermediate value 15! - expect([25]).toEqual(b.toArray()) - expect(mobx._isComputingDerivation()).toBe(false) - - done() - } catch (e) { - console.log(e.stack) - } -}) - -test("box uses equals", function (done) { - try { - const x = observable.box("a", { - equals: (oldValue, newValue) => { - return oldValue.toLowerCase() === newValue.toLowerCase() - } - }) - - const b = buffer() - m.observe(x, b) - - x.set("A") - x.set("b") - x.set("B") - x.set("C") - - expect(["b", "C"]).toEqual(b.toArray()) - expect(mobx._isComputingDerivation()).toBe(false) - - done() - } catch (e) { - console.log(e.stack) - } -}) - -test("box uses equals2", function (done) { - try { - const x = observable.box("01", { - equals: (oldValue, newValue) => { - return parseInt(oldValue) === parseInt(newValue) - } - }) - - const y = computed(function () { - return parseInt(x) - }) - - const b = buffer() - m.observe(y, b) - - x.set("2") - x.set("02") - x.set("002") - x.set("03") - - expect([2, 3]).toEqual(b.toArray()) - expect(mobx._isComputingDerivation()).toBe(false) - - done() - } catch (e) { - console.log(e.stack) - } -}) - -test("readme1", function (done) { - try { - const b = buffer() - - const vat = observable.box(0.2) - const order = {} - order.price = observable.box(10) - // Prints: New price: 24 - // in TS, just: value(() => this.price() * (1+vat())) - order.priceWithVat = computed(function () { - return order.price.get() * (1 + vat.get()) - }) - - m.observe(order.priceWithVat, b) - - order.price.set(20) - expect([24]).toEqual(b.toArray()) - order.price.set(10) - expect([24, 12]).toEqual(b.toArray()) - expect(mobx._isComputingDerivation()).toBe(false) - - done() - } catch (e) { - console.log(e.stack) - throw e - } -}) - -test("batch", function () { - const a = observable.box(2) - const b = observable.box(3) - const c = computed(function () { - return a.get() * b.get() - }) - const d = computed(function () { - return c.get() * b.get() - }) - const buf = buffer() - m.observe(d, buf) - - a.set(4) - b.set(5) - // Note, 60 should not happen! (that is d begin computed before c after update of b) - expect(buf.toArray()).toEqual([36, 100]) - - const x = mobx.transaction(function () { - a.set(2) - b.set(3) - a.set(6) - expect(d.value_).toBe(100) // not updated; in transaction - expect(d.get()).toBe(54) // consistent due to inspection - return 2 - }) - - expect(x).toBe(2) // test return value - expect(buf.toArray()).toEqual([36, 100, 54]) // only one new value for d -}) - -test("transaction with inspection", function () { - const a = observable.box(2) - let calcs = 0 - const b = computed(function () { - calcs++ - return a.get() * 2 - }) - - // if not inspected during transaction, postpone value to end - mobx.transaction(function () { - a.set(3) - expect(b.get()).toBe(6) - expect(calcs).toBe(1) - }) - expect(b.get()).toBe(6) - expect(calcs).toBe(2) - - // if inspected, evaluate eagerly - mobx.transaction(function () { - a.set(4) - expect(b.get()).toBe(8) - expect(calcs).toBe(3) - }) - expect(b.get()).toBe(8) - expect(calcs).toBe(4) -}) - -test("transaction with inspection 2", function () { - const a = observable.box(2) - let calcs = 0 - let b - mobx.autorun(function () { - calcs++ - b = a.get() * 2 - }) - - // if not inspected during transaction, postpone value to end - mobx.transaction(function () { - a.set(3) - expect(b).toBe(4) - expect(calcs).toBe(1) - }) - expect(b).toBe(6) - expect(calcs).toBe(2) - - // if inspected, evaluate eagerly - mobx.transaction(function () { - a.set(4) - expect(b).toBe(6) - expect(calcs).toBe(2) - }) - expect(b).toBe(8) - expect(calcs).toBe(3) -}) - -test("scope", function () { - const vat = observable.box(0.2) - const Order = function () { - this.price = observable.box(20) - this.amount = observable.box(2) - this.total = computed( - function () { - return (1 + vat.get()) * this.price.get() * this.amount.get() - }, - { context: this } - ) - } - - const order = new Order() - m.observe(order.total, voidObserver) - order.price.set(10) - order.amount.set(3) - expect(36).toBe(order.total.get()) - expect(mobx._isComputingDerivation()).toBe(false) -}) - -test("props1", function () { - const vat = observable.box(0.2) - const Order = function () { - mobx.extendObservable(this, { - price: 20, - amount: 2, - get total() { - return (1 + vat.get()) * this.price * this.amount // price and amount are now properties! - } - }) - } - - const order = new Order() - expect(48).toBe(order.total) - order.price = 10 - order.amount = 3 - expect(36).toBe(order.total) - - const totals = [] - const sub = mobx.autorun(function () { - totals.push(order.total) - }) - order.amount = 4 - sub() - order.amount = 5 - expect(totals).toEqual([36, 48]) - - expect(mobx._isComputingDerivation()).toBe(false) -}) - -test("props2", function () { - const vat = observable.box(0.2) - const Order = function () { - mobx.extendObservable(this, { - price: 20, - amount: 2, - get total() { - return (1 + vat.get()) * this.price * this.amount // price and amount are now properties! - } - }) - } - - const order = new Order() - expect(48).toBe(order.total) - order.price = 10 - order.amount = 3 - expect(36).toBe(order.total) -}) - -test("props4", function () { - function Bzz() { - mobx.extendObservable(this, { - fluff: [1, 2], - get sum() { - return this.fluff.reduce(function (a, b) { - return a + b - }, 0) - } - }) - } - - const x = new Bzz() - x.fluff - expect(x.sum).toBe(3) - x.fluff.push(3) - expect(x.sum).toBe(6) - x.fluff = [5, 6] - expect(x.sum).toBe(11) - x.fluff.push(2) - expect(x.sum).toBe(13) -}) - -test("object enumerable props", function () { - const x = mobx.observable({ - a: 3, - get b() { - return 2 * this.a - } - }) - mobx.extendObservable(x, { c: 4 }) - const ar = [] - for (const key in x) ar.push(key) - expect(ar).toEqual(["a", "c"]) -}) - -test("observe property", function () { - const sb = [] - const mb = [] - - const Wrapper = function (chocolateBar) { - mobx.extendObservable(this, { - chocolateBar: chocolateBar, - get calories() { - return this.chocolateBar.calories - } - }) - } - - const snickers = mobx.observable({ - calories: null - }) - const mars = mobx.observable({ - calories: undefined - }) - - const wrappedSnickers = new Wrapper(snickers) - const wrappedMars = new Wrapper(mars) - - const disposeSnickers = mobx.autorun(function () { - sb.push(wrappedSnickers.calories) - }) - const disposeMars = mobx.autorun(function () { - mb.push(wrappedMars.calories) - }) - snickers.calories = 10 - mars.calories = 15 - - disposeSnickers() - disposeMars() - snickers.calories = 5 - mars.calories = 7 - - expect(sb).toEqual([null, 10]) - expect(mb).toEqual([undefined, 15]) -}) - -test("observe object", function () { - let events = [] - const a = observable({ - a: 1, - get da() { - return this.a * 2 - } - }) - const stop = m.observe(a, function (change) { - expect(change.observableKind).toEqual("object") - delete change.observableKind - delete change.debugObjectName - events.push(change) - }) - - a.a = 2 - mobx.extendObservable(a, { - b: 3 - }) - a.a = 4 - a.b = 5 - expect(events).toEqual([ - { - type: "update", - object: a, - name: "a", - newValue: 2, - oldValue: 1 - }, - { - type: "add", - object: a, - newValue: 3, - name: "b" - }, - { - type: "update", - object: a, - name: "a", - newValue: 4, - oldValue: 2 - }, - { - type: "update", - object: a, - name: "b", - newValue: 5, - oldValue: 3 - } - ]) - - stop() - events = [] - a.a = 6 - expect(events.length).toBe(0) -}) - -test("mobx.observe", function () { - const events = [] - const o = observable({ b: 2 }) - const ar = observable([3]) - const map = mobx.observable.map({}) - - const push = function (event) { - delete event.debugObjectName - events.push(event) - } - - const stop2 = mobx.observe(o, push) - const stop3 = mobx.observe(ar, push) - const stop4 = mobx.observe(map, push) - - o.b = 5 - ar[0] = 6 - map.set("d", 7) - - stop2() - stop3() - stop4() - - o.b = 9 - ar[0] = 10 - map.set("d", 11) - - expect(events).toEqual([ - { - type: "update", - observableKind: "object", - object: o, - name: "b", - newValue: 5, - oldValue: 2 - }, - { - type: "update", - observableKind: "array", - object: ar, - index: 0, - newValue: 6, - oldValue: 3 - }, - { - type: "add", - observableKind: "map", - object: map, - newValue: 7, - name: "d" - } - ]) -}) - -test("change count optimization", function () { - let bCalcs = 0 - let cCalcs = 0 - const a = observable.box(3) - const b = computed(function () { - bCalcs += 1 - return 4 + a.get() - a.get() - }) - const c = computed(function () { - cCalcs += 1 - return b.get() - }) - - m.observe(c, voidObserver) - - expect(b.get()).toBe(4) - expect(c.get()).toBe(4) - expect(bCalcs).toBe(1) - expect(cCalcs).toBe(1) - - a.set(5) - - expect(b.get()).toBe(4) - expect(c.get()).toBe(4) - expect(bCalcs).toBe(2) - expect(cCalcs).toBe(1) - - expect(mobx._isComputingDerivation()).toBe(false) -}) - -test("observables removed", function () { - let calcs = 0 - const a = observable.box(1) - const b = observable.box(2) - const c = computed(function () { - calcs++ - if (a.get() === 1) return b.get() * a.get() * b.get() - return 3 - }) - - expect(calcs).toBe(0) - m.observe(c, voidObserver) - expect(c.get()).toBe(4) - expect(calcs).toBe(1) - a.set(2) - expect(c.get()).toBe(3) - expect(calcs).toBe(2) - - b.set(3) // should not retrigger calc - expect(c.get()).toBe(3) - expect(calcs).toBe(2) - - a.set(1) - expect(c.get()).toBe(9) - expect(calcs).toBe(3) - - expect(mobx._isComputingDerivation()).toBe(false) -}) - -test("lazy evaluation", function () { - let bCalcs = 0 - let cCalcs = 0 - let dCalcs = 0 - let observerChanges = 0 - - const a = observable.box(1) - const b = computed(function () { - bCalcs += 1 - return a.get() + 1 - }) - - const c = computed(function () { - cCalcs += 1 - return b.get() + 1 - }) - - expect(bCalcs).toBe(0) - expect(cCalcs).toBe(0) - expect(c.get()).toBe(3) - expect(bCalcs).toBe(1) - expect(cCalcs).toBe(1) - - expect(c.get()).toBe(3) - expect(bCalcs).toBe(2) - expect(cCalcs).toBe(2) - - a.set(2) - expect(bCalcs).toBe(2) - expect(cCalcs).toBe(2) - - expect(c.get()).toBe(4) - expect(bCalcs).toBe(3) - expect(cCalcs).toBe(3) - - const d = computed(function () { - dCalcs += 1 - return b.get() * 2 - }) - - const handle = m.observe( - d, - function () { - observerChanges += 1 - }, - false - ) - expect(bCalcs).toBe(4) - expect(cCalcs).toBe(3) - expect(dCalcs).toBe(1) // d is evaluated, so that its dependencies are known - - a.set(3) - expect(d.get()).toBe(8) - expect(bCalcs).toBe(5) - expect(cCalcs).toBe(3) - expect(dCalcs).toBe(2) - - expect(c.get()).toBe(5) - expect(bCalcs).toBe(5) - expect(cCalcs).toBe(4) - expect(dCalcs).toBe(2) - - expect(b.get()).toBe(4) - expect(bCalcs).toBe(5) - expect(cCalcs).toBe(4) - expect(dCalcs).toBe(2) - - handle() // unlisten - expect(d.get()).toBe(8) - expect(bCalcs).toBe(6) // gone to sleep - expect(cCalcs).toBe(4) - expect(dCalcs).toBe(3) - - expect(observerChanges).toBe(1) - - expect(mobx._isComputingDerivation()).toBe(false) -}) - -test("multiple view dependencies", function () { - let bCalcs = 0 - let dCalcs = 0 - const a = observable.box(1) - const b = computed(function () { - bCalcs++ - return 2 * a.get() - }) - const c = observable.box(2) - const d = computed(function () { - dCalcs++ - return 3 * c.get() - }) - - let zwitch = true - const buffer = [] - let fCalcs = 0 - const dis = mobx.autorun(function () { - fCalcs++ - if (zwitch) buffer.push(b.get() + d.get()) - else buffer.push(d.get() + b.get()) - }) - - zwitch = false - c.set(3) - expect(bCalcs).toBe(1) - expect(dCalcs).toBe(2) - expect(fCalcs).toBe(2) - expect(buffer).toEqual([8, 11]) - - c.set(4) - expect(bCalcs).toBe(1) - expect(dCalcs).toBe(3) - expect(fCalcs).toBe(3) - expect(buffer).toEqual([8, 11, 14]) - - dis() - c.set(5) - expect(bCalcs).toBe(1) - expect(dCalcs).toBe(3) - expect(fCalcs).toBe(3) - expect(buffer).toEqual([8, 11, 14]) -}) - -test("nested observable2", function () { - const factor = observable.box(0) - const price = observable.box(100) - let totalCalcs = 0 - let innerCalcs = 0 - - const total = computed(function () { - totalCalcs += 1 // outer observable shouldn't recalc if inner observable didn't publish a real change - return ( - price.get() * - computed(function () { - innerCalcs += 1 - return factor.get() % 2 === 0 ? 1 : 3 - }).get() - ) - }) - - const b = [] - m.observe( - total, - function (x) { - b.push(x.newValue) - }, - true - ) - - price.set(150) - factor.set(7) // triggers innerCalc twice, because changing the outcome triggers the outer calculation which recreates the inner calculation - factor.set(5) // doesn't trigger outer calc - factor.set(3) // doesn't trigger outer calc - factor.set(4) // triggers innerCalc twice - price.set(20) - - expect(b).toEqual([100, 150, 450, 150, 20]) - expect(innerCalcs).toBe(9) - expect(totalCalcs).toBe(5) -}) - -test("observe", function () { - const x = observable.box(3) - const x2 = computed(function () { - return x.get() * 2 - }) - const b = [] - - const cancel = mobx.autorun(function () { - b.push(x2.get()) - }) - - x.set(4) - x.set(5) - expect(b).toEqual([6, 8, 10]) - cancel() - x.set(7) - expect(b).toEqual([6, 8, 10]) -}) - -test("when", function () { - const x = observable.box(3) - - let called = 0 - mobx.when( - function () { - return x.get() === 4 - }, - function () { - called += 1 - } - ) - - x.set(5) - expect(called).toBe(0) - x.set(4) - expect(called).toBe(1) - x.set(3) - expect(called).toBe(1) - x.set(4) - expect(called).toBe(1) -}) - -test("when 2", function () { - const x = observable.box(3) - - let called = 0 - const d = mobx.when( - function () { - return x.get() === 3 - }, - function () { - called += 1 - }, - { name: "when x is 3" } - ) - - expect(called).toBe(1) - x.set(5) - x.set(3) - expect(called).toBe(1) - - expect(d[$mobx].name_).toBe("when x is 3") -}) - -function stripSpyOutput(events) { - events.forEach(ev => { - delete ev.time - delete ev.fn - delete ev.object - }) - return events -} - -test("issue 50", function (done) { - m._resetGlobalState() - mobx._getGlobalState().mobxGuid = 0 - const x = observable({ - a: true, - b: false, - get c() { - events.push("calc c") - return this.b - } - }) - - let result - const events = [] - const disposer1 = mobx.autorun(function ar() { - events.push("auto") - result = [x.a, x.b, x.c].join(",") - }) - - const disposer2 = mobx.spy(function (info) { - events.push(info) - }) - - setTimeout(function () { - mobx.transaction(function () { - events.push("transstart") - x.a = !x.a - x.b = !x.b - events.push("transpreend") - }) - events.push("transpostend") - expect(result).toBe("false,true,true") - expect(x.c).toBe(x.b) - - expect(stripSpyOutput(events)).toMatchSnapshot() - - disposer1() - disposer2() - done() - }, 500) -}) - -test("verify transaction events", function () { - m._resetGlobalState() - mobx._getGlobalState().mobxGuid = 0 - - const x = observable({ - b: 1, - get c() { - events.push("calc c") - return this.b - } - }) - - const events = [] - const disposer1 = mobx.autorun(function ar() { - events.push("auto") - x.c - }) - - const disposer2 = mobx.spy(function (info) { - events.push(info) - }) - - mobx.transaction(function () { - events.push("transstart") - x.b = 1 - x.b = 2 - events.push("transpreend") - }) - events.push("transpostend") - - expect(stripSpyOutput(events)).toMatchSnapshot() - - disposer1() - disposer2() -}) - -test("verify array in transaction", function () { - const ar = observable([]) - let aCount = 0 - let aValue - - mobx.autorun(function () { - aCount++ - aValue = 0 - for (let i = 0; i < ar.length; i++) aValue += ar[i] - }) - - mobx.transaction(function () { - ar.push(2) - ar.push(3) - ar.push(4) - ar.unshift(1) - }) - expect(aValue).toBe(10) - expect(aCount).toBe(2) -}) - -test("delay autorun until end of transaction", function () { - m._resetGlobalState() - mobx._getGlobalState().mobxGuid = 0 - const events = [] - const x = observable({ - a: 2, - get b() { - events.push("calc y") - return this.a - } - }) - let disposer1 - const disposer2 = mobx.spy(function (info) { - events.push(info) - }) - let didRun = false - - mobx.transaction(function () { - mobx.transaction(function () { - disposer1 = mobx.autorun(function test() { - didRun = true - events.push("auto") - x.b - }) - - expect(didRun).toBe(false) - - x.a = 3 - x.a = 4 - - events.push("end1") - }) - expect(didRun).toBe(false) - x.a = 5 - events.push("end2") - }) - - expect(didRun).toBe(true) - events.push("post trans1") - x.a = 6 - events.push("post trans2") - disposer1() - x.a = 3 - events.push("post trans3") - - expect(stripSpyOutput(events)).toMatchSnapshot() - - disposer2() -}) - -test("computed values believe NaN === NaN", function () { - const a = observable.box(2) - const b = observable.box(3) - const c = computed(function () { - return String(a.get() * b.get()) - }) - const buf = buffer() - m.observe(c, buf) - - a.set(NaN) - b.set(NaN) - a.set(NaN) - a.set(2) - b.set(3) - - expect(buf.toArray()).toEqual(["NaN", "6"]) -}) - -test("computed values believe deep NaN === deep NaN when using compareStructural", function () { - const a = observable({ b: { a: 1 } }) - const c = computed( - function () { - return a.b - }, - { compareStructural: true } - ) - - const buf = new buffer() - m.observe(c, newValue => { - buf(newValue) - }) - - a.b = { a: NaN } - a.b = { a: NaN } - a.b = { a: NaN } - a.b = { a: 2 } - a.b = { a: NaN } - - const bufArray = buf.toArray() - expect(isNaN(bufArray[0].b)).toBe(true) - expect(bufArray[1]).toEqual({ a: 2 }) - expect(isNaN(bufArray[2].b)).toEqual(true) - expect(bufArray.length).toBe(3) -}) - -test("issue 71, transacting running transformation", function () { - const state = mobx.observable({ - things: [] - }) - - function Thing(value) { - mobx.extendObservable(this, { - value: value, - get pos() { - return state.things.indexOf(this) - }, - get isVisible() { - return this.pos !== -1 - } - }) - - mobx.when( - () => { - return this.isVisible - }, - () => { - if (this.pos < 4) state.things.push(new Thing(value + 1)) - } - ) - } - - let copy - let vSum - mobx.autorun(function () { - copy = state.things.map(function (thing) { - return thing.value - }) - vSum = state.things.reduce(function (a, thing) { - return a + thing.value - }, 0) - }) - - expect(copy).toEqual([]) - - mobx.transaction(function () { - state.things.push(new Thing(1)) - }) - - expect(copy).toEqual([1, 2, 3, 4, 5]) - expect(vSum).toBe(15) - - state.things.splice(0, 2) - state.things.push(new Thing(6)) - - expect(copy).toEqual([3, 4, 5, 6, 7]) - expect(vSum).toBe(25) -}) - -test("eval in transaction", function () { - let bCalcs = 0 - const x = mobx.observable({ - a: 1, - get b() { - bCalcs++ - return this.a * 2 - } - }) - let c - - mobx.autorun(function () { - c = x.b - }) - - expect(bCalcs).toBe(1) - expect(c).toBe(2) - - mobx.transaction(function () { - x.a = 3 - expect(x.b).toBe(6) - expect(bCalcs).toBe(2) - expect(c).toBe(2) - - x.a = 4 - expect(x.b).toBe(8) - expect(bCalcs).toBe(3) - expect(c).toBe(2) - }) - expect(bCalcs).toBe(3) // 2 or 3 would be fine as well - expect(c).toBe(8) -}) - -test("forcefully tracked reaction should still yield valid results", function () { - const x = observable.box(3) - let z - let runCount = 0 - const identity = function () { - runCount++ - z = x.get() - } - const a = new mobx.Reaction("test", function () { - this.track(identity) - }) - a.runReaction_() - - expect(z).toBe(3) - expect(runCount).toBe(1) - - transaction(function () { - x.set(4) - a.track(identity) - expect(a.isScheduled).toBe(true) - expect(z).toBe(4) - expect(runCount).toBe(2) - }) - - expect(z).toBe(4) - expect(runCount).toBe(2) // x is observed, so it should recompute only on dependency change - - transaction(function () { - x.set(5) - expect(a.isScheduled).toBe(true) - a.track(identity) - expect(z).toBe(5) - expect(runCount).toBe(3) - expect(a.isScheduled).toBe(true) - - x.set(6) - expect(z).toBe(5) - expect(runCount).toBe(3) - }) - expect(a.isScheduled).toBe(false) - expect(z).toBe(6) - expect(runCount).toBe(4) -}) - -test("autoruns created in autoruns should kick off", function () { - const x = observable.box(3) - const x2 = [] - let d - - const a = m.autorun(function () { - if (d) { - // dispose previous autorun - d() - } - d = m.autorun(function () { - x2.push(x.get() * 2) - }) - }) - - // a should be observed by the inner autorun, not the outer - expect(a[$mobx].observing_.length).toBe(0) - expect(d[$mobx].observing_.length).toBe(1) - - x.set(4) - expect(x2).toEqual([6, 8]) -}) - -test("#502 extendObservable throws on objects created with Object.create(null)", () => { - const a = Object.create(null) - mobx.extendObservable(a, { b: 3 }) - expect(mobx.isObservableProp(a, "b")).toBe(true) -}) - -test("#328 atom throwing exception if observing stuff in onObserved", () => { - const b = mobx.observable.box(1) - const a = mobx.createAtom("test atom", () => { - b.get() - }) - const d = mobx.autorun(() => { - a.reportObserved() // threw - }) - d() -}) - -test("prematurely ended autoruns are cleaned up properly", () => { - const a = mobx.observable.box(1) - const b = mobx.observable.box(2) - const c = mobx.observable.box(3) - let called = 0 - - const d = mobx.autorun(() => { - called++ - if (a.get() === 2) { - d() // dispose - b.get() // consume - a.set(3) // cause itself to re-run, but, disposed! - } else { - c.get() - } - }) - - expect(called).toBe(1) - - a.set(2) - - expect(called).toBe(2) -}) - -test("unoptimizable subscriptions are diffed correctly", () => { - const a = mobx.observable.box(1) - const b = mobx.observable.box(1) - const c = mobx.computed(() => { - a.get() - return 3 - }) - let called = 0 - let val = 0 - - const d = mobx.autorun(() => { - called++ - a.get() - c.get() // reads a as well - val = a.get() - if ( - b.get() === 1 // only on first run - ) - a.get() // second run: one read less for a - }) - - expect(called).toBe(1) - expect(val).toBe(1) - - b.set(2) - - expect(called).toBe(2) - expect(val).toBe(1) - - a.set(2) - - expect(called).toBe(3) - expect(val).toBe(2) - - d() -}) - -test("atom events #427", () => { - let start = 0 - let stop = 0 - let runs = 0 - - const a = mobx.createAtom( - "test", - () => start++, - () => stop++ - ) - expect(a.reportObserved()).toEqual(false) - - expect(start).toBe(0) - expect(stop).toBe(0) - - let d = mobx.autorun(() => { - runs++ - expect(a.reportObserved()).toBe(true) - expect(start).toBe(1) - expect(a.reportObserved()).toBe(true) - expect(start).toBe(1) - }) - - expect(runs).toBe(1) - expect(start).toBe(1) - expect(stop).toBe(0) - a.reportChanged() - expect(runs).toBe(2) - expect(start).toBe(1) - expect(stop).toBe(0) - - d() - expect(runs).toBe(2) - expect(start).toBe(1) - expect(stop).toBe(1) - - expect(a.reportObserved()).toBe(false) - expect(start).toBe(1) - expect(stop).toBe(1) - - d = mobx.autorun(() => { - expect(a.reportObserved()).toBe(true) - expect(start).toBe(2) - a.reportObserved() - expect(start).toBe(2) - }) - - expect(start).toBe(2) - expect(stop).toBe(1) - a.reportChanged() - expect(start).toBe(2) - expect(stop).toBe(1) - - d() - expect(stop).toBe(2) -}) - -test("verify calculation count", () => { - const calcs = [] - const a = observable.box(1) - const b = mobx.computed(() => { - calcs.push("b") - return a.get() - }) - const c = mobx.computed(() => { - calcs.push("c") - return b.get() - }) - const d = mobx.autorun(() => { - calcs.push("d") - return b.get() - }) - const e = mobx.autorun(() => { - calcs.push("e") - return c.get() - }) - const f = mobx.computed(() => { - calcs.push("f") - return c.get() - }) - - expect(f.get()).toBe(1) - - calcs.push("change") - a.set(2) - - expect(f.get()).toBe(2) - - calcs.push("transaction") - transaction(() => { - expect(b.get()).toBe(2) - expect(c.get()).toBe(2) - expect(f.get()).toBe(2) - expect(f.get()).toBe(2) - calcs.push("change") - a.set(3) - expect(b.get()).toBe(3) - expect(b.get()).toBe(3) - calcs.push("try c") - expect(c.get()).toBe(3) - expect(c.get()).toBe(3) - calcs.push("try f") - expect(f.get()).toBe(3) - expect(f.get()).toBe(3) - calcs.push("end transaction") - }) - - expect(calcs).toEqual([ - "d", - "b", - "e", - "c", - "f", - "change", - "b", - "d", - "c", - "e", - "f", // would have expected b c e d f, but alas - "transaction", - "f", - "change", - "b", - "try c", - "c", - "try f", - "f", - "end transaction", - "d", - "e" - ]) - - d() - e() -}) - -test("support computed property getters / setters", () => { - let a = observable({ - size: 1, - get volume() { - return this.size * this.size - } - }) - - expect(a.volume).toBe(1) - a.size = 3 - expect(a.volume).toBe(9) - - expect(() => (a.volume = 9)).toThrow( - /It is not possible to assign a new value to a computed value/ - ) - - a = {} - mobx.extendObservable(a, { - size: 2, - get volume() { - return this.size * this.size - }, - set volume(v) { - this.size = Math.sqrt(v) - } - }) - - const values = [] - const d = mobx.autorun(() => values.push(a.volume)) - - a.volume = 9 - mobx.transaction(() => { - a.volume = 100 - a.volume = 64 - }) - - expect(values).toEqual([4, 9, 64]) - expect(a.size).toEqual(8) - - d() -}) - -test("computed getter / setter for plan objects should succeed", function () { - const b = observable({ - a: 3, - get propX() { - return this.a * 2 - }, - set propX(v) { - this.a = v - } - }) - - const values = [] - mobx.autorun(function () { - return values.push(b.propX) - }) - expect(b.propX).toBe(6) - b.propX = 4 - expect(b.propX).toBe(8) - - expect(values).toEqual([6, 8]) -}) - -test("helpful error for self referencing setter", function () { - const a = observable({ - x: 1, - get y() { - return this.x - }, - set y(v) { - this.y = v // woops...;-) - } - }) - - expect(() => (a.y = 2)).toThrow(/The setter of computed value/) -}) - -test("#558 boxed observables stay boxed observables", function () { - const a = observable({ - x: observable.box(3) - }) - - expect(typeof a.x).toBe("object") - expect(typeof a.x.get).toBe("function") -}) - -test("iscomputed", function () { - expect(mobx.isComputed(observable.box(3))).toBe(false) - expect( - mobx.isComputed( - mobx.computed(function () { - return 3 - }) - ) - ).toBe(true) - - const x = observable({ - a: 3, - get b() { - return this.a - } - }) - - expect(mobx.isComputedProp(x, "a")).toBe(false) - expect(mobx.isComputedProp(x, "b")).toBe(true) -}) - -test("603 - transaction should not kill reactions", () => { - const a = observable.box(1) - let b = 1 - const d = mobx.autorun(() => { - b = a.get() - }) - - try { - mobx.transaction(() => { - a.set(2) - throw 3 - }) - } catch (e) { - // empty - } - - const g = m._getGlobalState() - expect(g.inBatch).toEqual(0) - expect(g.pendingReactions.length).toEqual(0) - expect(g.pendingUnobservations.length).toEqual(0) - expect(g.trackingDerivation).toEqual(null) - - expect(b).toBe(2) - a.set(3) - expect(b).toBe(3) - d() -}) - -test("#561 test toPrimitive() of observable objects", function () { - if (typeof Symbol !== "undefined" && Symbol.toPrimitive) { - let x = observable.box(3) - - expect(x.valueOf()).toBe(3) - expect(x[Symbol.toPrimitive]()).toBe(3) - - expect(+x).toBe(3) - expect(++x).toBe(4) - - const y = observable.box(3) - - expect(y + 7).toBe(10) - - const z = computed(() => ({ a: 3 })) - expect(3 + z).toBe("3[object Object]") - } else { - let x = observable.box(3) - - expect(x.valueOf()).toBe(3) - expect(x["@@toPrimitive"]()).toBe(3) - - expect(+x).toBe(3) - expect(++x).toBe(4) - - const y = observable.box(3) - - expect(y + 7).toBe(10) - - const z = computed(() => ({ a: 3 })) - expect("3" + z["@@toPrimitive"]()).toBe("3[object Object]") - } -}) - -test("computed equals function only invoked when necessary", () => { - utils.supressConsole(() => { - const comparisons = [] - const loggingComparer = (from, to) => { - comparisons.push({ from, to }) - return from === to - } - - const left = mobx.observable.box("A") - const right = mobx.observable.box("B") - const combinedToLowerCase = mobx.computed( - () => left.get().toLowerCase() + right.get().toLowerCase(), - { equals: loggingComparer } - ) - - const values = [] - let disposeAutorun = mobx.autorun(() => values.push(combinedToLowerCase.get())) - - // No comparison should be made on the first value - expect(comparisons).toEqual([]) - - // First change will cause a comparison - left.set("C") - expect(comparisons).toEqual([{ from: "ab", to: "cb" }]) - - // Transition *to* CaughtException in the computed won't cause a comparison - left.set(null) - expect(comparisons).toEqual([{ from: "ab", to: "cb" }]) - - // Transition *between* CaughtException-s in the computed won't cause a comparison - right.set(null) - expect(comparisons).toEqual([{ from: "ab", to: "cb" }]) - - // Transition *from* CaughtException in the computed won't cause a comparison - left.set("D") - right.set("E") - expect(comparisons).toEqual([{ from: "ab", to: "cb" }]) - - // Another value change will cause a comparison - right.set("F") - expect(comparisons).toEqual([ - { from: "ab", to: "cb" }, - { from: "de", to: "df" } - ]) - - // Becoming unobserved, then observed won't cause a comparison - disposeAutorun() - disposeAutorun = mobx.autorun(() => values.push(combinedToLowerCase.get())) - expect(comparisons).toEqual([ - { from: "ab", to: "cb" }, - { from: "de", to: "df" } - ]) - - expect(values).toEqual(["ab", "cb", "de", "df", "df"]) - - disposeAutorun() - }) -}) - -// document that extendObservable is not inheritance compatible, -// and make sure this does work with decorate -test("Issue 1092 - Should not access attributes of siblings in the prot. chain", () => { - // The parent is an observable - // and has an attribute - const parent = {} - mobx.extendObservable(parent, { - staticObservable: 11 - }) - - // Child1 "inherit" from the parent - // and has an observable attribute - const child1 = Object.create(parent) - mobx.extendObservable(child1, { - attribute: 7 - }) - - // Child2 also "inherit" from the parent - // But does not have any observable attribute - const child2 = Object.create(parent) - - // The second child should not be aware of the attribute of his - // sibling child1 - expect(typeof child2.attribute).toBe("undefined") - - expect(parent.staticObservable).toBe(11) - parent.staticObservable = 12 - expect(parent.staticObservable).toBe(12) -}) - -test("Issue 1092 - We should be able to define observable on all siblings", () => { - expect.assertions(1) - - // The parent is an observable - const parent = {} - mobx.extendObservable(parent, {}) - - // Child1 "inherit" from the parent - // and has an observable attribute - const child1 = Object.create(parent) - mobx.extendObservable(child1, { - attribute: 7 - }) - - // Child2 also "inherit" from the parent - // But does not have any observable attribute - const child2 = Object.create(parent) - expect(() => { - mobx.extendObservable(child2, { - attribute: 8 - }) - }).not.toThrow() -}) - -test("Issue 1120 - isComputed should return false for a non existing property", () => { - expect(mobx.isComputedProp({}, "x")).toBe(false) - expect(mobx.isComputedProp(observable({}), "x")).toBe(false) -}) - -test("computed comparer works with decorate (plain)", () => { - const sameTime = (from, to) => from.hour === to.hour && from.minute === to.minute - function Time(hour, minute) { - this.hour = hour - this.minute = minute - makeObservable(this, { - hour: observable, - minute: observable, - time: computed({ equals: sameTime }) - }) - } - - Object.defineProperty(Time.prototype, "time", { - configurable: true, - enumerable: true, - get() { - return { hour: this.hour, minute: this.minute } - } - }) - const time = new Time(9, 0) - - const changes = [] - const disposeAutorun = autorun(() => changes.push(time.time)) - - expect(changes).toEqual([{ hour: 9, minute: 0 }]) - time.hour = 9 - expect(changes).toEqual([{ hour: 9, minute: 0 }]) - time.minute = 0 - expect(changes).toEqual([{ hour: 9, minute: 0 }]) - time.hour = 10 - expect(changes).toEqual([ - { hour: 9, minute: 0 }, - { hour: 10, minute: 0 } - ]) - time.minute = 30 - expect(changes).toEqual([ - { hour: 9, minute: 0 }, - { hour: 10, minute: 0 }, - { hour: 10, minute: 30 } - ]) - - disposeAutorun() -}) - -test("computed comparer works with decorate (plain) - 2", () => { - const sameTime = (from, to) => from.hour === to.hour && from.minute === to.minute - function Time(hour, minute) { - extendObservable( - this, - { - hour, - minute, - get time() { - return { hour: this.hour, minute: this.minute } - } - }, - { - time: computed({ equals: sameTime }) - } - ) - } - const time = new Time(9, 0) - - const changes = [] - const disposeAutorun = autorun(() => changes.push(time.time)) - - expect(changes).toEqual([{ hour: 9, minute: 0 }]) - time.hour = 9 - expect(changes).toEqual([{ hour: 9, minute: 0 }]) - time.minute = 0 - expect(changes).toEqual([{ hour: 9, minute: 0 }]) - time.hour = 10 - expect(changes).toEqual([ - { hour: 9, minute: 0 }, - { hour: 10, minute: 0 } - ]) - time.minute = 30 - expect(changes).toEqual([ - { hour: 9, minute: 0 }, - { hour: 10, minute: 0 }, - { hour: 10, minute: 30 } - ]) - - disposeAutorun() -}) - -test("computed comparer works with decorate (plain) - 3", () => { - const sameTime = (from, to) => from.hour === to.hour && from.minute === to.minute - const time = observable.object( - { - hour: 9, - minute: 0, - get time() { - return { hour: this.hour, minute: this.minute } - } - }, - { - time: computed({ equals: sameTime }) - } - ) - - const changes = [] - const disposeAutorun = autorun(() => changes.push(time.time)) - - expect(changes).toEqual([{ hour: 9, minute: 0 }]) - time.hour = 9 - expect(changes).toEqual([{ hour: 9, minute: 0 }]) - time.minute = 0 - expect(changes).toEqual([{ hour: 9, minute: 0 }]) - time.hour = 10 - expect(changes).toEqual([ - { hour: 9, minute: 0 }, - { hour: 10, minute: 0 } - ]) - time.minute = 30 - expect(changes).toEqual([ - { hour: 9, minute: 0 }, - { hour: 10, minute: 0 }, - { hour: 10, minute: 30 } - ]) - - disposeAutorun() -}) - -test("can create computed with setter", () => { - let y = 1 - let x = mobx.computed(() => y, { - set: v => { - y = v * 2 - } - }) - expect(x.get()).toBe(1) - x.set(3) - expect(x.get()).toBe(6) -}) - -test("can make non-extenible objects observable", () => { - const base = { x: 3 } - Object.freeze(base) - const o = mobx.observable(base) - o.x = 4 - expect(o.x).toBe(4) - expect(mobx.isObservableProp(o, "x")).toBeTruthy() -}) - -test("keeping computed properties alive does not run before access", () => { - let calcs = 0 - observable( - { - x: 1, - get y() { - calcs++ - return this.x * 2 - } - }, - { - y: mobx.computed({ keepAlive: true }) - } - ) - - expect(calcs).toBe(0) // initially there is no calculation done -}) - -test("(for objects) keeping computed properties alive does not run before access", () => { - let calcs = 0 - class Foo { - @observable x = 1 - - constructor() { - makeObservable(this) - } - - @computed({ keepAlive: true }) - get y() { - calcs++ - return this.x * 2 - } - } - new Foo() - - expect(calcs).toBe(0) // initially there is no calculation done -}) - -test("keeping computed properties alive runs on first access", () => { - let calcs = 0 - const x = observable( - { - x: 1, - get y() { - calcs++ - return this.x * 2 - } - }, - { - y: mobx.computed({ keepAlive: true }) - } - ) - - expect(calcs).toBe(0) - expect(x.y).toBe(2) // perform calculation on access - expect(calcs).toBe(1) -}) - -test("keeping computed properties alive caches values on subsequent accesses", () => { - let calcs = 0 - const x = observable( - { - x: 1, - get y() { - calcs++ - return this.x * 2 - } - }, - { - y: mobx.computed({ keepAlive: true }) - } - ) - - expect(x.y).toBe(2) // first access: do calculation - expect(x.y).toBe(2) // second access: use cached value, no calculation - expect(calcs).toBe(1) // only one calculation: cached! -}) - -test("keeping computed properties alive does not recalculate when dirty", () => { - let calcs = 0 - const x = observable( - { - x: 1, - get y() { - calcs++ - return this.x * 2 - } - }, - { - y: mobx.computed({ keepAlive: true }) - } - ) - - expect(x.y).toBe(2) // first access: do calculation - expect(calcs).toBe(1) - x.x = 3 // mark as dirty: no calculation - expect(calcs).toBe(1) - expect(x.y).toBe(6) -}) - -test("keeping computed properties alive recalculates when accessing it dirty", () => { - let calcs = 0 - const x = observable( - { - x: 1, - get y() { - calcs++ - return this.x * 2 - } - }, - { - y: mobx.computed({ keepAlive: true }) - } - ) - - expect(x.y).toBe(2) // first access: do calculation - expect(calcs).toBe(1) - x.x = 3 // mark as dirty: no calculation - expect(calcs).toBe(1) - expect(x.y).toBe(6) // second access: do calculation because it is dirty - expect(calcs).toBe(2) -}) - -test("(for objects) keeping computed properties alive recalculates when accessing it dirty", () => { - let calcs = 0 - class Foo { - @observable x = 1 - - constructor() { - makeObservable(this) - } - - @computed({ keepAlive: true }) - get y() { - calcs++ - return this.x * 2 - } - } - const x = new Foo() - - expect(x.y).toBe(2) // first access: do calculation - expect(calcs).toBe(1) - x.x = 3 // mark as dirty: no calculation - expect(calcs).toBe(1) - expect(x.y).toBe(6) // second access: do calculation because it is dirty - expect(calcs).toBe(2) -}) - -test("tuples", () => { - // See #1391 - function tuple() { - const res = new Array(arguments.length) - for (let i = 0; i < arguments.length; i++) mobx.extendObservable(res, { [i]: arguments[i] }) - return res - } - - const myStuff = tuple(1, 3) - const events = [] - - mobx.reaction( - () => myStuff[0], - val => events.push(val) - ) - myStuff[1] = 17 // should not react - myStuff[0] = 2 // should react - expect(events).toEqual([2]) - - expect(myStuff.map(x => x * 2)).toEqual([4, 34]) -}) diff --git a/packages/mobx/__tests__/v4/base/observe.ts b/packages/mobx/__tests__/v4/base/observe.ts deleted file mode 100644 index fc7328517c..0000000000 --- a/packages/mobx/__tests__/v4/base/observe.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { observable, observe, computed } from "../mobx4" - -test("observe object and map properties", function () { - const map = observable.map({ a: 1 }) - const events: any[] = [] - - expect(() => observe(map, "b", () => {})).toThrow( - /the entry 'b' does not exist in the observable map/ - ) - - const d1 = observe(map, "a", e => events.push([e.newValue, e.oldValue])) - - map.set("a", 2) - map.set("a", 3) - d1() - map.set("a", 4) - - const o = observable({ a: 5 }) - - expect(() => observe(o, "b" as any, () => {})).toThrow( - /no observable property 'b' found on the observable object/ - ) - const d2 = observe(o, "a", e => events.push([e.newValue, e.oldValue])) - - o.a = 6 - o.a = 7 - d2() - o.a = 8 - - expect(events).toEqual([ - [2, 1], - [3, 2], - [6, 5], - [7, 6] - ]) -}) - -test("observe computed values", () => { - const events: any[] = [] - - const v = observable.box(0) - const f = observable.box(0) - const c = computed(() => v.get()) - - observe(c, e => { - v.get() - f.get() - events.push([e.newValue, e.oldValue]) - }) - - v.set(6) - f.set(10) - - expect(events).toEqual([[6, 0]]) -}) diff --git a/packages/mobx/__tests__/v4/base/spy.js b/packages/mobx/__tests__/v4/base/spy.js deleted file mode 100644 index 78291fee6d..0000000000 --- a/packages/mobx/__tests__/v4/base/spy.js +++ /dev/null @@ -1,101 +0,0 @@ -"use strict" -const mobx = require("../mobx4") -const utils = require("../../v5/utils/test-utils") - -test("spy output", () => { - const events = [] - - const stop = mobx.spy(c => events.push(c)) - - doStuff() - - stop() - - doStuff() - - events.forEach(ev => { - delete ev.object - delete ev.fn - delete ev.time - }) - - expect(events).toMatchSnapshot() -}) - -function doStuff() { - const a = mobx.observable.box(2) - a.set(3) - - const b = mobx.observable({ - c: 4 - }) - b.c = 5 - mobx.extendObservable(b, { d: 6 }) - b.d = 7 - - const e = mobx.observable([1, 2]) - e.push(3, 4) - e.shift() - e[2] = 5 - - const f = mobx.observable.map({ g: 1 }) - f.delete("h") - f.delete("g") - f.set("i", 5) - f.set("i", 6) - - const j = mobx.computed(() => a.get() * 2) - - mobx.autorun(() => { - j.get() - }) - - a.set(4) - - mobx.transaction(function myTransaction() { - a.set(5) - a.set(6) - }) - - mobx.action("myTestAction", newValue => { - a.set(newValue) - }).call({}, 7) -} - -test("spy error", () => { - utils.supressConsole(() => { - mobx._getGlobalState().mobxGuid = 0 - - const a = mobx.observable({ - x: 2, - get y() { - if (this.x === 3) throw "Oops" - return this.x * 2 - } - }) - - const events = [] - const stop = mobx.spy(c => events.push(c)) - - const d = mobx.autorun(() => a.y, { name: "autorun" }) - - a.x = 3 - - events.forEach(x => { - delete x.fn - delete x.object - delete x.time - }) - - expect(events).toMatchSnapshot() - - d() - stop() - }) -}) - -test("spy stop listen from handler, #1459", () => { - const stop = mobx.spy(() => stop()) - mobx.spy(() => {}) - doStuff() -}) diff --git a/packages/mobx/__tests__/v4/base/tojs.js b/packages/mobx/__tests__/v4/base/tojs.js deleted file mode 100644 index 81bd93f8ba..0000000000 --- a/packages/mobx/__tests__/v4/base/tojs.js +++ /dev/null @@ -1,366 +0,0 @@ -"use strict" - -const mobx = require("../mobx4") -const m = mobx -const observable = mobx.observable -const { makeObservable } = mobx - -test("json1", function () { - mobx._resetGlobalState() - - const todos = observable([ - { - title: "write blog" - }, - { - title: "improve coverge" - } - ]) - - let output - mobx.autorun(function () { - output = todos - .map(function (todo) { - return todo.title - }) - .join(", ") - }) - - todos[1].title = "improve coverage" // prints: write blog, improve coverage - expect(output).toBe("write blog, improve coverage") - todos.push({ title: "take a nap" }) // prints: write blog, improve coverage, take a nap - expect(output).toBe("write blog, improve coverage, take a nap") -}) - -test("json2", function () { - const source = { - todos: [ - { - title: "write blog", - tags: ["react", "frp"], - details: { - url: "somewhere" - } - }, - { - title: "do the dishes", - tags: ["mweh"], - details: { - url: "here" - } - } - ] - } - - const o = mobx.observable(JSON.parse(JSON.stringify(source))) - - expect(mobx.toJS(o)).toEqual(source) - - const analyze = mobx.computed(function () { - return [o.todos.length, o.todos[1].details.url] - }) - - const alltags = mobx.computed(function () { - return o.todos - .map(function (todo) { - return todo.tags.join(",") - }) - .join(",") - }) - - let ab = [] - let tb = [] - - m.observe( - analyze, - function (d) { - ab.push(d.newValue) - }, - true - ) - m.observe( - alltags, - function (d) { - tb.push(d.newValue) - }, - true - ) - - o.todos[0].details.url = "boe" - o.todos[1].details.url = "ba" - o.todos[0].tags[0] = "reactjs" - o.todos[1].tags.push("pff") - - expect(mobx.toJS(o)).toEqual({ - todos: [ - { - title: "write blog", - tags: ["reactjs", "frp"], - details: { - url: "boe" - } - }, - { - title: "do the dishes", - tags: ["mweh", "pff"], - details: { - url: "ba" - } - } - ] - }) - expect(ab).toEqual([ - [2, "here"], - [2, "ba"] - ]) - expect(tb).toEqual(["react,frp,mweh", "reactjs,frp,mweh", "reactjs,frp,mweh,pff"]) - ab = [] - tb = [] - - o.todos.push( - mobx.observable({ - title: "test", - tags: ["x"] - }) - ) - - expect(mobx.toJS(o)).toEqual({ - todos: [ - { - title: "write blog", - tags: ["reactjs", "frp"], - details: { - url: "boe" - } - }, - { - title: "do the dishes", - tags: ["mweh", "pff"], - details: { - url: "ba" - } - }, - { - title: "test", - tags: ["x"] - } - ] - }) - expect(ab).toEqual([[3, "ba"]]) - expect(tb).toEqual(["reactjs,frp,mweh,pff,x"]) - ab = [] - tb = [] - - o.todos[1] = mobx.observable({ - title: "clean the attic", - tags: ["needs sabbatical"], - details: { - url: "booking.com" - } - }) - expect(JSON.parse(JSON.stringify(o))).toEqual({ - todos: [ - { - title: "write blog", - tags: ["reactjs", "frp"], - details: { - url: "boe" - } - }, - { - title: "clean the attic", - tags: ["needs sabbatical"], - details: { - url: "booking.com" - } - }, - { - title: "test", - tags: ["x"] - } - ] - }) - expect(ab).toEqual([[3, "booking.com"]]) - expect(tb).toEqual(["reactjs,frp,needs sabbatical,x"]) - ab = [] - tb = [] - - o.todos[1].details = mobx.observable({ url: "google" }) - o.todos[1].tags = ["foo", "bar"] - expect(mobx.toJS(o)).toEqual({ - todos: [ - { - title: "write blog", - tags: ["reactjs", "frp"], - details: { - url: "boe" - } - }, - { - title: "clean the attic", - tags: ["foo", "bar"], - details: { - url: "google" - } - }, - { - title: "test", - tags: ["x"] - } - ] - }) - expect(mobx.toJS(o)).toEqual(mobx.toJS(o)) - expect(ab).toEqual([[3, "google"]]) - expect(tb).toEqual(["reactjs,frp,foo,bar,x"]) -}) - -test("toJS handles dates", () => { - const a = observable({ - d: new Date() - }) - - const b = mobx.toJS(a) - expect(b.d instanceof Date).toBe(true) - expect(a.d === b.d).toBe(true) -}) - -test("json cycles", function () { - const a = observable({ - b: 1, - c: [2], - d: mobx.observable.map() - }) - - a.e = a - a.c.push(a, a.d) - a.d.set("f", a) - a.d.set("d", a.d) - a.d.set("c", a.c) - - const cloneA = mobx.toJS(a) - const cloneC = cloneA.c - const cloneD = cloneA.d - - expect(cloneA.b).toBe(1) - expect(cloneA.c[0]).toBe(2) - expect(cloneA.c[1]).toBe(cloneA) - expect(cloneA.c[2]).toBe(cloneD) - expect(cloneD.get("f")).toBe(cloneA) - expect(cloneD.get("d")).toBe(cloneD) - expect(cloneD.get("c")).toBe(cloneC) - expect(cloneA.e).toBe(cloneA) -}) - -test("#285 class instances with toJS", () => { - function Person() { - this.firstName = "michel" - mobx.extendObservable(this, { - lastName: "weststrate", - tags: ["user", "mobx-member"], - get fullName() { - return this.firstName + this.lastName - } - }) - } - - const p1 = new Person() - // check before lazy initialization - expect(mobx.toJS(p1)).toEqual({ - firstName: "michel", - lastName: "weststrate", - tags: ["user", "mobx-member"] - }) - - // check after lazy initialization - expect(mobx.toJS(p1)).toEqual({ - firstName: "michel", - lastName: "weststrate", - tags: ["user", "mobx-member"] - }) -}) - -test("#285 non-mobx class instances with toJS", () => { - const nameObservable = mobx.observable.box("weststrate") - function Person() { - this.firstName = "michel" - this.lastName = nameObservable - } - - const p1 = new Person() - // check before lazy initialization - expect(mobx.toJS(p1)).toEqual({ - firstName: "michel", - lastName: nameObservable // toJS doesn't recurse into non observable objects! - }) -}) - -test("verify #566 solution", () => { - function MyClass() {} - const a = new MyClass() - const b = mobx.observable({ x: 3 }) - const c = mobx.observable({ a: a, b: b }) - - expect(mobx.toJS(c).a === a).toBeTruthy() // true - expect(mobx.toJS(c).b !== b).toBeTruthy() // false, cloned - expect(mobx.toJS(c).b.x === b.x).toBeTruthy() // true, both 3 -}) - -test("verify already seen", () => { - const a = mobx.observable({ x: null, y: 3 }) - a.x = a - - const res = mobx.toJS(a) - expect(res.y).toBe(3) - expect(res.x === res).toBeTruthy() - expect(res.x === a).toBeFalsy() -}) - -test("json cycles when exporting maps as maps", function () { - const a = observable({ - b: 1, - c: [2], - d: mobx.observable.map() - }) - - a.e = a - a.c.push(a, a.d) - a.d.set("f", a) - a.d.set("d", a.d) - a.d.set("c", a.c) - - const cloneA = mobx.toJS(a) - const cloneC = cloneA.c - const cloneD = cloneA.d - - expect(cloneA.b).toBe(1) - expect(cloneA.c[0]).toBe(2) - expect(cloneA.c[1]).toBe(cloneA) - expect(cloneA.c[2]).toBe(cloneD) - expect(cloneD).toBeInstanceOf(Map) - expect(cloneD.get("f")).toBe(cloneA) - expect(cloneD.get("d")).toBe(cloneD) - expect(cloneD.get("c")).toBe(cloneC) - expect(cloneA.e).toBe(cloneA) -}) - -test("map to JS", () => { - class MyClass { - @observable meta = new Map() - - constructor() { - makeObservable(this) - this.meta.set("test", { abc: "def", ghi: "jkl" }) - - expect(mobx.toJS(this.meta).constructor.name).toBe("Map") - } - } - new MyClass() -}) - -test("Correctly converts observable objects with computed values", () => { - const a = observable({ key: "value" }) - const c = observable({ computedValue: mobx.computed(() => a.key) }) - - const j = mobx.toJS(c) - expect(j).toMatchObject({ computedValue: "value" }) -}) diff --git a/packages/mobx/__tests__/v4/base/untracked.ts b/packages/mobx/__tests__/v4/base/untracked.ts deleted file mode 100644 index 4f1c960284..0000000000 --- a/packages/mobx/__tests__/v4/base/untracked.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { autorun, observable, computed, untracked } from "../mobx4" - -test("untracked 1", () => { - let cCalcs = 0, - dCalcs = 0 - const a = observable.box(1) - const b = observable.box(2) - const c = computed(() => { - cCalcs++ - return a.get() + untracked(() => b.get()) - }) - let result - - autorun(() => { - dCalcs++ - result = c.get() - }) - - expect(result).toBe(3) - expect(cCalcs).toBe(1) - expect(dCalcs).toBe(1) - - b.set(3) - expect(result).toBe(3) - expect(cCalcs).toBe(1) - expect(dCalcs).toBe(1) - - a.set(2) - expect(result).toBe(5) - expect(cCalcs).toBe(2) - expect(dCalcs).toBe(2) -}) diff --git a/packages/mobx/__tests__/v4/mobx4.ts b/packages/mobx/__tests__/v4/mobx4.ts deleted file mode 100644 index f6c0134ee6..0000000000 --- a/packages/mobx/__tests__/v4/mobx4.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { configure } from "../../src/mobx" - -configure({ - useProxies: "never", - enforceActions: "never" -}) - -export * from "../../src/mobx" diff --git a/packages/mobx/__tests__/v5/base/__snapshots__/makereactive.js.snap b/packages/mobx/__tests__/v5/base/__snapshots__/makereactive.js.snap deleted file mode 100644 index 8cb8eface6..0000000000 --- a/packages/mobx/__tests__/v5/base/__snapshots__/makereactive.js.snap +++ /dev/null @@ -1,3 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`computed value 1`] = `"ComputedValue@1[() => 3]"`; diff --git a/packages/mobx/__tests__/v5/base/__snapshots__/observables.js.snap b/packages/mobx/__tests__/v5/base/__snapshots__/observables.js.snap deleted file mode 100644 index 1fc3d3ed56..0000000000 --- a/packages/mobx/__tests__/v5/base/__snapshots__/observables.js.snap +++ /dev/null @@ -1,209 +0,0 @@ -// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing - -exports[`delay autorun until end of transaction 1`] = ` -[ - { - "debugObjectName": "ObservableObject@1", - "name": "a", - "newValue": 3, - "observableKind": "object", - "oldValue": 2, - "spyReportStart": true, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "debugObjectName": "ObservableObject@1", - "name": "a", - "newValue": 4, - "observableKind": "object", - "oldValue": 3, - "spyReportStart": true, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - "end1", - { - "debugObjectName": "ObservableObject@1", - "name": "a", - "newValue": 5, - "observableKind": "object", - "oldValue": 4, - "spyReportStart": true, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - "end2", - { - "name": "test", - "spyReportStart": true, - "type": "reaction", - }, - "auto", - "calc y", - { - "debugObjectName": "ObservableObject@1.b", - "newValue": 5, - "observableKind": "computed", - "oldValue": CaughtException { - "cause": null, - }, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - "post trans1", - { - "debugObjectName": "ObservableObject@1", - "name": "a", - "newValue": 6, - "observableKind": "object", - "oldValue": 5, - "spyReportStart": true, - "type": "update", - }, - "calc y", - { - "debugObjectName": "ObservableObject@1.b", - "newValue": 6, - "observableKind": "computed", - "oldValue": 5, - "type": "update", - }, - { - "name": "test", - "spyReportStart": true, - "type": "reaction", - }, - "auto", - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - "post trans2", - { - "debugObjectName": "ObservableObject@1", - "name": "a", - "newValue": 3, - "observableKind": "object", - "oldValue": 6, - "spyReportStart": true, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - "post trans3", -] -`; - -exports[`issue 50 1`] = ` -[ - "auto", - "calc c", - "transstart", - { - "debugObjectName": "ObservableObject@1", - "name": "a", - "newValue": false, - "observableKind": "object", - "oldValue": true, - "spyReportStart": true, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - { - "debugObjectName": "ObservableObject@1", - "name": "b", - "newValue": true, - "observableKind": "object", - "oldValue": false, - "spyReportStart": true, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - "transpreend", - { - "name": "ar", - "spyReportStart": true, - "type": "reaction", - }, - "auto", - "calc c", - { - "debugObjectName": "ObservableObject@1.c", - "newValue": true, - "observableKind": "computed", - "oldValue": false, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - "transpostend", -] -`; - -exports[`verify transaction events 1`] = ` -[ - "auto", - "calc c", - "transstart", - { - "debugObjectName": "ObservableObject@1", - "name": "b", - "newValue": 2, - "observableKind": "object", - "oldValue": 1, - "spyReportStart": true, - "type": "update", - }, - { - "spyReportEnd": true, - "type": "report-end", - }, - "transpreend", - "calc c", - { - "debugObjectName": "ObservableObject@1.c", - "newValue": 2, - "observableKind": "computed", - "oldValue": 1, - "type": "update", - }, - { - "name": "ar", - "spyReportStart": true, - "type": "reaction", - }, - "auto", - { - "spyReportEnd": true, - "type": "report-end", - }, - "transpostend", -] -`; diff --git a/packages/mobx/__tests__/v5/base/cycles.js b/packages/mobx/__tests__/v5/base/cycles.js deleted file mode 100644 index 7733865899..0000000000 --- a/packages/mobx/__tests__/v5/base/cycles.js +++ /dev/null @@ -1,194 +0,0 @@ -const m = require("../../../src/mobx.ts") - -test("cascading active state (form 1)", function () { - const Store = function () { - m.extendObservable(this, { _activeItem: null }) - } - Store.prototype.activeItem = function (item) { - const _this = this - - if (arguments.length === 0) return this._activeItem - - m.transaction(function () { - if (_this._activeItem === item) return - if (_this._activeItem) _this._activeItem.isActive = false - _this._activeItem = item - if (_this._activeItem) _this._activeItem.isActive = true - }) - } - - const Item = function () { - m.extendObservable(this, { isActive: false }) - } - - const store = new Store() - const item1 = new Item(), - item2 = new Item() - expect(store.activeItem()).toBe(null) - expect(item1.isActive).toBe(false) - expect(item2.isActive).toBe(false) - - store.activeItem(item1) - expect(store.activeItem()).toBe(item1) - expect(item1.isActive).toBe(true) - expect(item2.isActive).toBe(false) - - store.activeItem(item2) - expect(store.activeItem()).toBe(item2) - expect(item1.isActive).toBe(false) - expect(item2.isActive).toBe(true) - - store.activeItem(null) - expect(store.activeItem()).toBe(null) - expect(item1.isActive).toBe(false) - expect(item2.isActive).toBe(false) -}) - -test("cascading active state (form 2)", function () { - const Store = function () { - const _this = this - m.extendObservable(this, { activeItem: null }) - - m.autorun(function () { - if (_this._activeItem === _this.activeItem) return - if (_this._activeItem) _this._activeItem.isActive = false - _this._activeItem = _this.activeItem - if (_this._activeItem) _this._activeItem.isActive = true - }) - } - - const Item = function () { - m.extendObservable(this, { isActive: false }) - } - - const store = new Store() - const item1 = new Item(), - item2 = new Item() - expect(store.activeItem).toBe(null) - expect(item1.isActive).toBe(false) - expect(item2.isActive).toBe(false) - - store.activeItem = item1 - expect(store.activeItem).toBe(item1) - expect(item1.isActive).toBe(true) - expect(item2.isActive).toBe(false) - - store.activeItem = item2 - expect(store.activeItem).toBe(item2) - expect(item1.isActive).toBe(false) - expect(item2.isActive).toBe(true) - - store.activeItem = null - expect(store.activeItem).toBe(null) - expect(item1.isActive).toBe(false) - expect(item2.isActive).toBe(false) -}) - -test("emulate rendering", function () { - let renderCount = 0 - - const Component = function (props) { - this.props = props - } - Component.prototype.destroy = function () { - if (this.handler) { - this.handler() - this.handler = null - } - } - - Component.prototype.render = function () { - const _this = this - - if (this.handler) { - this.handler() - this.handler = null - } - this.handler = m.autorun(function () { - if (!_this.props.data.title) _this.props.data.title = "HELLO" - renderCount++ - }) - } - - const data = {} - m.extendObservable(data, { title: null }) - const component = new Component({ data: data }) - expect(renderCount).toBe(0) - - component.render() - expect(renderCount).toBe(1) - - data.title = "WORLD" - expect(renderCount).toBe(2) - - data.title = null - // Note that this causes two invalidations - // however, the real mobx-react binding optimizes this as well - // see mobx-react #12, so maybe this ain't the best test - expect(renderCount).toBe(4) - - data.title = "WORLD" - expect(renderCount).toBe(5) - - component.destroy() - data.title = "HELLO" - expect(renderCount).toBe(5) -}) - -test("efficient selection", function () { - function Item(value) { - m.extendObservable(this, { - selected: false, - value: value - }) - } - - function Store() { - this.prevSelection = null - m.extendObservable(this, { - selection: null, - items: [new Item(1), new Item(2), new Item(3)] - }) - m.autorun(() => { - if (this.previousSelection === this.selection) return true // converging condition - if (this.previousSelection) this.previousSelection.selected = false - if (this.selection) this.selection.selected = true - this.previousSelection = this.selection - }) - } - - const store = new Store() - - expect(store.selection).toBe(null) - expect( - store.items.filter(function (i) { - return i.selected - }).length - ).toBe(0) - - store.selection = store.items[1] - expect( - store.items.filter(function (i) { - return i.selected - }).length - ).toBe(1) - expect(store.selection).toBe(store.items[1]) - expect(store.items[1].selected).toBe(true) - - store.selection = store.items[2] - expect( - store.items.filter(function (i) { - return i.selected - }).length - ).toBe(1) - expect(store.selection).toBe(store.items[2]) - expect(store.items[2].selected).toBe(true) - - store.selection = null - expect( - store.items.filter(function (i) { - return i.selected - }).length - ).toBe(0) - expect(store.selection).toBe(null) -}) diff --git a/packages/mobx/__tests__/v5/base/nested.js b/packages/mobx/__tests__/v5/base/nested.js deleted file mode 100644 index 60f8299e82..0000000000 --- a/packages/mobx/__tests__/v5/base/nested.js +++ /dev/null @@ -1,87 +0,0 @@ -"use strict" - -import { extendObservable, observable, autorun, computed, runInAction } from "../../../src/mobx" - -test("nested computeds should not run unnecessary", () => { - function Item(name) { - extendObservable(this, { - name: name, - get index() { - const i = store.items.indexOf(this) - if (i === -1) throw "not found" - return i - } - }) - } - - const store = observable({ - items: [], - get asString() { - return this.items.map(item => item.index + ":" + item.name).join(",") - } - }) - store.items.push(new Item("item1")) - - const values = [] - autorun(() => { - values.push(store.asString) - }) - - store.items.replace([new Item("item2")]) - - expect(values).toEqual(["0:item1", "0:item2"]) -}) - -test("fix #1535: stale observables", cb => { - // see https://codesandbox.io/s/k92o2jmz63 - const snapshots = [] - - const x = observable.box(1) - - // Depends on observable x - const derived1 = computed(() => { - return x.get() + 1 - }) - - // Depends on computed derived1 - const derived2 = computed(() => { - return derived1.get() + 1 - }) - - function increment() { - runInAction(() => { - x.set(x.get() + 1) - // No problems here - derived1.get() - derived2.get() - }) - } - - function brokenIncrement() { - runInAction(() => x.set(x.get() + 1)) - // Acessing computed outside of action causes staleness - // NOTE IT DOESN'T MATTER WHICH COMPUTED IS ACCESSED - // derived1.get(); - derived2.get() - } - - autorun( - () => { - snapshots.push(`${x.get()}, ${derived1.get()}, ${derived2.get()}`) - }, - { - scheduler(f) { - setImmediate(f) - } - } - ) - - increment() - setTimeout(() => { - brokenIncrement() - }, 100) - setTimeout(() => { - expect(snapshots).toEqual(["2, 3, 4", "3, 4, 5"]) - cb() - }, 1000) -}) diff --git a/packages/mobx/__tests__/v5/flow/test.js b/packages/mobx/__tests__/v5/flow/test.js deleted file mode 100644 index a84a35cefe..0000000000 --- a/packages/mobx/__tests__/v5/flow/test.js +++ /dev/null @@ -1,47 +0,0 @@ -//@flow - -import type { IObservableValue, IObservableArray, IComputedValue } from "../../../dist/index.js" -import * as mobx from "../../../dist/index.js" - -const action = mobx.action(() => console.log(1)) -// $ExpectError -const isAction: string = mobx.isAction(action) - -const observableValue: IObservableValue = mobx.observable.box(1) -// $ExpectError -const initialValue: string = observableValue.get() - -const observableArray: IObservableArray = mobx.observable([1, 2, 3]) -// $ExpectError -const initialArray: Array = observableArray.peek() - -const sum: IComputedValue = mobx.computed(() => { - return observableArray.reduce((a: number, b: number): number => { - return a + b - }, 0) -}) - -const observableObject = mobx.observable({ - a: true -}) -// $ExpectError -observableObject.a = 12 -// $ExpectError -observableObject.b = 12 -observableObject.a = false - -const extendedObservableObject = mobx.extendObservable(mobx.observable({}), { a: true }) -// $ExpectError -const x: string = extendedObservableObject.a - -const disposer = mobx.autorun(() => console.log(sum.get())) -disposer() - -const emptyArray = observable([]) -const arr = observable([1]) -const object = observable.map({ - nestedValue: arr -}) - -object.get("nestedValue").push(1) -const y: number = object.get("nestedValue")[0] diff --git a/packages/mobx/flow-typed/mobx.js b/packages/mobx/flow-typed/mobx.js deleted file mode 100644 index c8fa08c653..0000000000 --- a/packages/mobx/flow-typed/mobx.js +++ /dev/null @@ -1,591 +0,0 @@ -// @flow - -export type IObservableMapInitialValues = IMapEntries | KeyValueMap | IMap - -export interface IMobxConfigurationOptions { - +enforceActions?: "never" | "always" | "observed"; - computedRequiresReaction?: boolean; - /** - * (Experimental) - * Warn if you try to create to derivation / reactive context without accessing any observable. - */ - reactionRequiresObservable?: boolean; - /** - * (Experimental) - * Warn if observables are accessed outside a reactive context - */ - observableRequiresReaction?: boolean; - isolateGlobalState?: boolean; - disableErrorBoundaries?: boolean; - reactionScheduler?: (f: () => void) => void; - useProxies?: "always" | "never" | "ifavailable"; -} - -declare export function configure(options: IMobxConfigurationOptions): void - -export interface IAutorunOptions { - delay?: number; - name?: string; - /** - * warn if the derivation has no dependencies after creation/update - */ - requiresObservable?: boolean; - scheduler?: (callback: () => void) => any; - onError?: (error: any) => void; -} - -export interface IReactionOptions extends IAutorunOptions { - fireImmediately?: boolean; - equals?: IEqualsComparer; -} - -export interface IInterceptable { - interceptors: IInterceptor[] | any; - intercept(handler: IInterceptor): Lambda; -} - -export type IEqualsComparer = (a: T, b: T) => boolean - -export type IInterceptor = (change: T) => T - -export type IMapEntry = [K, V] - -export type IMapEntries = IMapEntry[] - -export interface IMap { - clear(): void; - delete(key: K): boolean; - forEach(callbackfn: (value: V, index: K, map: IMap) => void, thisArg?: any): void; - get(key: K): V | any; - has(key: K): boolean; - set(key: K, value?: V): any; - size: number; -} - -declare export function isObservableMap(x: any): boolean - -export interface IComputedValueOptions { - get?: () => T; - set?: (value: T) => void; - name?: string; - equals?: IEqualsComparer; - context?: any; -} - -type PropertyDescriptor = { - enumerable?: boolean, - configurable?: boolean, - writable?: boolean, - value?: T, - get?: () => T, - set?: (value: T) => void -} - -export interface IComputed { - (func: () => T, setter?: (value: T) => void): IComputedValue; - (func: () => T, options: IComputedValueOptions): IComputedValue; - (target: Object, key: string, baseDescriptor?: PropertyDescriptor<*>): void; - struct(target: Object, key: string, baseDescriptor?: PropertyDescriptor<*>): void; -} - -export interface IDependencyTree { - name: string; - dependencies?: IDependencyTree[]; -} - -export interface IObserverTree { - name: string; - observers?: IObserverTree[]; -} - -export interface IAtom { - reportObserved: () => void; - reportChanged: () => void; -} - -export interface IComputedValue { - get(): T; - set(value: T): void; - observe(listener: (newValue: T, oldValue: T) => void, fireImmediately?: boolean): Lambda; -} - -export interface IObservable {} - -export interface IDepTreeNode { - name: string; - observing?: IObservable[]; -} - -export interface IDerivation { - name: string; -} - -export interface IReactionPublic { - dispose: () => void; - trace: (enterBreakPoint?: boolean) => void; -} - -declare export class IListenable { - observe(handler: (change: any, oldValue?: any) => void, fireImmediately?: boolean): Lambda; -} - -export interface IObservableArray extends Array { - spliceWithArray(index: number, deleteCount?: number, newItems?: T[]): T[]; - observe( - listener: (changeData: IArrayChange | IArraySplice) => void, - fireImmediately?: boolean - ): Lambda; - intercept(handler: IInterceptor | IArrayWillSplice>): Lambda; - intercept(handler: IInterceptor | IArraySplice>): Lambda; // TODO: remove in 4.0 - intercept(handler: IInterceptor | IArraySplice>): Lambda; // TODO: remove in 4.0 - clear(): T[]; - peek(): T[]; - replace(newItems: T[]): T[]; - find( - predicate: (item: T, index: number, array: Array) => mixed, - thisArg?: any, - fromIndex?: number - ): T | any; - findIndex( - predicate: (item: T, index: number, array: Array) => mixed, - thisArg?: any, - fromIndex?: number - ): number; - remove(value: T): boolean; -} - -export interface IArrayChange { - type: "update"; - object: IObservableArray; - index: number; - newValue: T; - oldValue: T; -} - -export interface IArraySplice { - type: "splice"; - object: IObservableArray; - index: number; - added: T[]; - addedCount: number; - removed: T[]; - removedCount: number; -} - -export interface IArrayWillChange { - type: "update"; - object: IObservableArray; - index: number; - newValue: T; -} - -export interface IArrayWillSplice { - type: "splice"; - object: IObservableArray; - index: number; - added: T[]; - removedCount: number; -} - -export type KeyValueMap = { - [key: string]: V -} - -export interface IMapChange { - object: ObservableMap; - type: "update" | "add" | "delete"; - name: K; - newValue?: any; - oldValue?: any; -} - -export interface IMapWillChange { - object: ObservableMap; - type: "update" | "add" | "delete"; - name: K; - newValue?: any; -} - -export interface IObservableObject {} - -export interface IObjectChange { - name: string; - object: any; - type: "update" | "add" | "remove"; - oldValue?: any; - newValue: any; -} - -export interface IObjectWillChange { - object: any; - type: "update" | "add" | "remove"; - name: string; - newValue: any; -} - -export interface IValueWillChange { - object: any; - type: "update"; - newValue: T; -} - -export interface IValueDidChange extends IValueWillChange { - oldValue: ?T; -} - -export interface IObservableValue { - get(): T; - set(value: T): void; - intercept(handler: IInterceptor>): Lambda; - observe(listener: (change: IValueDidChange) => void, fireImmediately?: boolean): Lambda; -} - -export interface IEnhancer { - (newValue: T, oldValue: T | void, name: string): T; -} - -export interface IObservableFactory { - // observable overloads - (target: Object, key: string, baseDescriptor?: PropertyDescriptor<*>): any; - (value: Array): IObservableArray; - (value: null | void): IObservableValue; - (value: null | void): IObservableValue; - (value: IMap): ObservableMap; - (value: T): T; -} - -export type IObservableDecorator = { - (target: Object, property: string, descriptor?: PropertyDescriptor<*>): void, - enhancer: IEnhancer -} - -export type CreateObservableOptions = { - name?: string, - deep?: boolean, - defaultDecorator?: IObservableDecorator -} - -declare export class IObservableFactories { - box(value?: T, options?: CreateObservableOptions): IObservableValue; - array(initialValues?: T[], options?: CreateObservableOptions): IObservableArray; - set(initialValues?: V[], options?: CreateObservableOptions): Set; - map( - initialValues?: IObservableMapInitialValues, - options?: CreateObservableOptions - ): ObservableMap; - object(props: T, options?: CreateObservableOptions): T & IObservableObject; - ref( - target: Object, - property?: string, - descriptor?: PropertyDescriptor<*> - ): IObservableDecorator; - shallow( - target: Object, - property?: string, - descriptor?: PropertyDescriptor<*> - ): IObservableDecorator; - deep( - target: Object, - property?: string, - descriptor?: PropertyDescriptor<*> - ): IObservableDecorator; - struct( - target: Object, - property?: string, - descriptor?: PropertyDescriptor<*> - ): IObservableDecorator; -} - -export interface Lambda { - (): void; - name?: string; -} - -export interface IActionFactory { - (a1: any, a2?: any, a3?: any, a4?: any, a6?: any): any; - bound(target: Object, propertyKey: string, descriptor?: PropertyDescriptor<*>): void; -} - -declare export class ObservableMap { - constructor(initialData?: IMapEntries | KeyValueMap, valueModeFunc?: Function): this; - has(key: K): boolean; - set(key: K, value: V): void; - delete(key: K): boolean; - get(key: K): V; - keys(): Iterator; - values(): Iterator; - entries(): IMapEntries & Iterator>; - forEach(callback: (value: V, key: K, object: KeyValueMap) => void, thisArg?: any): void; - merge(other: ObservableMap | KeyValueMap): ObservableMap; - clear(): void; - replace(other: ObservableMap | KeyValueMap): ObservableMap; - size: number; - toJS(): Map; - toPOJO(): KeyValueMap; - toJSON(): KeyValueMap; - toString(): string; - observe(listener: (changes: IMapChange) => void, fireImmediately?: boolean): Lambda; - intercept(handler: IInterceptor>): Lambda; -} - -declare export function action( - targetOrName: any, - propertyKeyOrFuc?: any, - descriptor?: PropertyDescriptor<*> -): any -declare export function action(name: string, func: T): T -declare export function action(func: T): T - -declare export function runInAction(name: string, block: () => T): T -declare export function runInAction(block: () => T): T -declare export function isAction(thing: any): boolean -declare export function autorun( - nameOrFunction: string | ((r: IReactionPublic) => any), - options?: IAutorunOptions -): any -declare export function reaction( - expression: (r: IReactionPublic) => T, - effect: (arg: T, r: IReactionPublic) => void, - opts?: IReactionOptions -): () => mixed - -export interface IWhenOptions { - name?: string; - timeout?: number; - onError?: (error: any) => void; - /** - * warn if the derivation has no dependencies after creation/update - */ - requiresObservable?: boolean; -} - -declare export function when( - cond: () => boolean, - effect: Lambda, - options?: IWhenOptions -): () => mixed -declare export function when(cond: () => boolean, options?: IWhenOptions): Promise - -declare export function computed( - target: any, - key?: string, - baseDescriptor?: PropertyDescriptor<*> -): any - -declare export function extendObservable( - target: A, - properties: B, - decorators?: any, - options?: any -): A & B - -declare export function makeObservable(target: A, annotations?: any, options: any): A - -declare export function makeAutoObservable(target: A, exceptions?: any, options: any): A - -declare export function intercept( - object: Object, - property: string, - handler: IInterceptor -): Lambda - -declare export function isComputed(value: any): boolean -declare export function isComputedProp(value: any, property: string): boolean - -declare export function isObservable(value: any): boolean -declare export function isObservableProp(value: any, property: string): boolean - -declare export var comparer: { - identity: IEqualsComparer, - structural: IEqualsComparer, - default: IEqualsComparer -} - -declare export var observable: IObservableFactory & - IObservableFactories & { - deep: { - struct(initialValue?: T): T - }, - ref: { - struct(initialValue?: T): T - } - } - -declare export function observe( - value: IObservableValue | IComputedValue, - listener: (change: IValueDidChange) => void, - fireImmediately?: boolean -): Lambda -declare export function observe( - observableArray: IObservableArray, - listener: (change: IArrayChange | IArraySplice) => void, - fireImmediately?: boolean -): Lambda -declare export function observe( - observableMap: ObservableMap, - listener: (change: IMapChange) => void, - fireImmediately?: boolean -): Lambda -declare export function observe( - observableMap: ObservableMap, - property: string, - listener: (change: IValueDidChange) => void, - fireImmediately?: boolean -): Lambda -declare export function observe( - object: any, - listener: (change: IObjectChange) => void, - fireImmediately?: boolean -): Lambda -declare export function observe( - object: any, - property: string, - listener: (change: IValueDidChange) => void, - fireImmediately?: boolean -): Lambda - -declare export function toJS(source: T): T - -declare export function untracked(action: () => T): T - -declare export function spy(listener: (change: any) => void): Lambda - -declare export function transaction(action: () => T, thisArg?: any, report?: boolean): T - -declare export function isObservableArray(thing: any): boolean - -declare export function isObservableObject(thing: T): boolean - -declare export class Reaction { - name: string; - isDisposed: boolean; - constructor(name: string, onInvalidate: () => void): this; - schedule(): void; - isScheduled(): boolean; - track(fn: () => void): void; - dispose(): void; - getDisposer(): Lambda & { - $mosbservable: Reaction - }; - toString(): string; - trace(enterBreakPoint?: boolean): void; -} - -declare export function createAtom( - name: string, - onBecomeObservedHandler?: () => void, - onBecomeUnobservedHandler?: () => void -): IAtom - -declare export function decorate(target: T, decorators: any): T - -export type CancellablePromise = Promise & { cancel: () => void } - -declare export function flow( - fn: () => Generator, any> | AsyncGenerator, any> -): () => CancellablePromise -declare export function flow( - fn: (A) => Generator, any> | AsyncGenerator, any> -): A => CancellablePromise -declare export function flow( - fn: (A, B) => Generator, any> | AsyncGenerator, any> -): (A, B) => CancellablePromise -declare export function flow( - fn: (A, B, C) => Generator, any> | AsyncGenerator, any> -): (A, B, C) => CancellablePromise -declare export function flow( - fn: ( - A, - B, - C, - D - ) => Generator, any> | AsyncGenerator, any> -): (A, B, C, D) => CancellablePromise -declare export function flow( - fn: ( - A, - B, - C, - D, - E - ) => Generator, any> | AsyncGenerator, any> -): (A, B, C, D, E) => CancellablePromise -declare export function flow( - fn: ( - A, - B, - C, - D, - E, - F - ) => Generator, any> | AsyncGenerator, any> -): (A, B, C, D, E, F) => CancellablePromise -declare export function flow( - fn: ( - A, - B, - C, - D, - E, - F, - G, - H - ) => Generator, any> | AsyncGenerator, any> -): (A, B, C, D, E, F, G, H) => CancellablePromise - -declare export function isFlowCancellationError(error: Error): boolean -declare export class FlowCancellationError extends Error {} - -declare export function keys(map: ObservableMap): K[] -declare export function keys(obj: any): string[] - -declare export function values(map: ObservableMap): T[] -declare export function values(ar: IObservableArray): T[] -declare export function values(obj: any): any[] - -declare export function set(obj: ObservableMap, values: { [key: string]: V }): void -declare export function set(obj: ObservableMap, key: K, value: V): void -declare export function set(obj: IObservableArray, index: number, value: T): void -declare export function set(obj: any, values: { [key: string]: any }): void -declare export function set(obj: any, key: string, value: any): void - -declare export function remove(obj: ObservableMap, key: K): void -declare export function remove(obj: IObservableArray, index: number): void -declare export function remove(obj: any, key: string): void - -declare export function has(obj: ObservableMap, key: K): boolean -declare export function has(obj: IObservableArray, index: number): boolean -declare export function has(obj: any, key: string): boolean - -declare export function get(obj: ObservableMap, key: K): V | void -declare export function get(obj: IObservableArray, index: number): T | void -declare export function get(obj: any, key: string): any - -declare export function onReactionError( - handler: (error: any, derivation: IDerivation) => void -): () => void - -declare export function onBecomeObserved( - value: IObservable | IComputedValue | IObservableArray | ObservableMap, - listener: Lambda -): Lambda -declare export function onBecomeObserved( - value: ObservableMap | Object, - property: K, - listener: Lambda -): Lambda - -declare export function onBecomeUnobserved( - value: IObservable | IComputedValue | IObservableArray | ObservableMap, - listener: Lambda -): Lambda -declare export function onBecomeUnobserved( - value: ObservableMap | Object, - property: K, - listener: Lambda -): Lambda - -declare export function getAtom(thing: any, property?: string): IDepTreeNode -declare export function getDebugName(thing: any, property?: string): string -declare export function getDependencyTree(thing: any, property?: string): IDependencyTree -declare export function getObserverTree(thing: any, property?: string): IObserverTree diff --git a/packages/mobx/jest.config.js b/packages/mobx/jest.config.js index f0af2b1e22..e502a78982 100644 --- a/packages/mobx/jest.config.js +++ b/packages/mobx/jest.config.js @@ -1,6 +1,6 @@ const buildConfig = require("../../jest.base.config") module.exports = buildConfig(__dirname, { - testRegex: "__tests__/v[4|5]/base/.*\\.(t|j)sx?$", + testRegex: "__tests__/base/.*\\.(t|j)sx?$", setupFilesAfterEnv: [`/jest.setup.ts`] }) diff --git a/packages/mobx/jest.setup.ts b/packages/mobx/jest.setup.ts index 74251d4e79..38da0a0ab9 100644 --- a/packages/mobx/jest.setup.ts +++ b/packages/mobx/jest.setup.ts @@ -1,12 +1,25 @@ -import { configure, _resetGlobalState } from "./src/mobx" +import { configure, _getGlobalState, _resetGlobalState } from "./src/mobx" global.setImmediate = global.setImmediate || ((fn, ...args) => global.setTimeout(fn, 0, ...args)) -beforeEach(() => { - // @ts-ignore - global.__DEV__ = true +function resetMobxTestState() { _resetGlobalState() + _getGlobalState().spyListeners = [] configure({ - enforceActions: "never" + useProxies: "always", + enforceActions: "never", + computedRequiresReaction: false, + reactionRequiresObservable: false, + observableRequiresReaction: false, + disableErrorBoundaries: false, + safeDescriptors: true }) +} + +beforeEach(() => { + // @ts-ignore + global.__DEV__ = true + resetMobxTestState() }) + +afterEach(resetMobxTestState) diff --git a/packages/mobx/package-lock.json b/packages/mobx/package-lock.json index 7c06a9e092..7a43cfce43 100644 --- a/packages/mobx/package-lock.json +++ b/packages/mobx/package-lock.json @@ -1,6 +1,6 @@ { "name": "mobx", - "version": "6.1.6", + "version": "6.16.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1201,12 +1201,6 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, - "flow-bin": { - "version": "0.123.0", - "resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.123.0.tgz", - "integrity": "sha512-Ylcf8YDIM/KrqtxkPuq+f8O+6sdYA2Nuz5f+sWHlp539DatZz3YMcsO1EiXaf1C11HJgpT/3YGYe7xZ9/UZmvQ==", - "dev": true - }, "function-bind": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", diff --git a/packages/mobx/package.json b/packages/mobx/package.json index 9f3455de7a..e4db9abfce 100644 --- a/packages/mobx/package.json +++ b/packages/mobx/package.json @@ -1,6 +1,6 @@ { "name": "mobx", - "version": "6.16.0", + "version": "6.16.1", "description": "Simple, scalable state management.", "source": "src/mobx.ts", "main": "dist/index.js", @@ -43,8 +43,7 @@ "@babel/preset-env": "^7.9.5", "@babel/preset-typescript": "^7.9.0", "@babel/runtime": "^7.9.2", - "conditional-type-checks": "^1.0.5", - "flow-bin": "^0.123.0" + "conditional-type-checks": "^1.0.5" }, "keywords": [ "mobx", @@ -72,7 +71,6 @@ "test:performance": "npm run perf -- proxy && npm run perf -- legacy && npm run perf-decorator", "test:mixed-versions": "npm test -- --testRegex mixed-versions", "test:types": "tsc --noEmit", - "test:flow": "flow check", "test:coverage": "npm test -- -i --coverage", "test:size": "import-size --report . observable computed autorun action", "test:check": "npm run test:types", diff --git a/packages/mobx/scripts/prepublish.js b/packages/mobx/scripts/prepublish.js index 279ee15df6..f7041ed6a5 100644 --- a/packages/mobx/scripts/prepublish.js +++ b/packages/mobx/scripts/prepublish.js @@ -3,7 +3,6 @@ const path = require("path") const execa = require("execa") const run = () => { - fs.copySync("flow-typed/mobx.js", "dist/index.js.flow") fs.copySync("../../README.md", "./README.md") fs.copySync("../../LICENSE", "./LICENSE") } diff --git a/website/static/js/scripts.js b/website/static/js/scripts.js index df0c0746ba..0447820e9b 100644 --- a/website/static/js/scripts.js +++ b/website/static/js/scripts.js @@ -9,6 +9,86 @@ function openTarget() { }, 150) } } + +function slugify(value) { + return value + .toLowerCase() + .replace(/`/g, '') + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, ''); +} + +function assignLinkableTabIds() { + var tabs = document.querySelectorAll('.nav-link[data-group][data-tab]'); + var assigned = {}; + + for (var tab of tabs) { + var id = slugify(tab.textContent); + if (!id) { + continue; + } + + var uniqueId = id; + var suffix = 2; + while (assigned[uniqueId] || document.getElementById(uniqueId)) { + uniqueId = id + '-' + suffix++; + } + + assigned[uniqueId] = true; + tab.id = uniqueId; + } +} + +function activateTab(tab) { + var groupId = tab.getAttribute('data-group'); + var contentId = tab.getAttribute('data-tab'); + + for (var groupTab of document.querySelectorAll(".nav-link[data-group='" + groupId + "']")) { + groupTab.classList.remove('active'); + } + + for (var pane of document.querySelectorAll(".tab-pane[data-group='" + groupId + "']")) { + pane.classList.remove('active'); + } + + tab.classList.add('active'); + + var content = document.getElementById(contentId); + if (content) { + content.classList.add('active'); + } +} + +function openLinkedTab() { + var hash = location.hash.substring(1); + if (!hash) { + return false; + } + + var tab = document.getElementById(hash); + if (!tab || !tab.matches('.nav-link[data-group][data-tab]')) { + return false; + } + + activateTab(tab); + setTimeout(function() { + tab.scrollIntoView(); + }, 150); + return true; +} + +function addHashToTabClicks() { + var tabs = document.querySelectorAll('.nav-link[data-group][data-tab]'); + + for (var tab of tabs) { + tab.addEventListener('click', function(e) { + if (e.currentTarget.id) { + location.hash = e.currentTarget.id; + } + }); + } +} + function addTooltipToRockets() { var classNames = ['navGroups', 'onPageNav', 'post', 'docs-prevnext']; var rocketRegex = /🚀/g; @@ -20,8 +100,16 @@ function addTooltipToRockets() { } } } -window.addEventListener('hashchange', openTarget); +window.addEventListener('hashchange', function() { + if (!openLinkedTab()) { + openTarget(); + } +}); window.addEventListener('DOMContentLoaded', function() { addTooltipToRockets(); - openTarget(); + assignLinkableTabIds(); + addHashToTabClicks(); + if (!openLinkedTab()) { + openTarget(); + } });