Best Python code snippet using avocado_python
storeStressTestConcurrent-test.js
Source:storeStressTestConcurrent-test.js  
1/**2 * Copyright (c) Facebook, Inc. and its affiliates.3 *4 * This source code is licensed under the MIT license found in the5 * LICENSE file in the root directory of this source tree.6 *7 * @flow8 */9describe('StoreStressConcurrent', () => {10  let React;11  let ReactDOM;12  let act;13  let actAsync;14  let bridge;15  let store;16  let print;17  beforeEach(() => {18    bridge = global.bridge;19    store = global.store;20    store.collapseNodesByDefault = false;21    React = require('react');22    ReactDOM = require('react-dom');23    act = require('./utils').act;24    // TODO: Figure out recommendation for concurrent mode tests, then replace25    // this helper with the real thing.26    actAsync = require('./utils').actAsync;27    print = require('./storeSerializer').print;28  });29  // TODO: Remove this in favor of @gate pragma30  if (!__EXPERIMENTAL__) {31    it("empty test so Jest doesn't complain", () => {});32    return;33  }34  // This is a stress test for the tree mount/update/unmount traversal.35  // It renders different trees that should produce the same output.36  it('should handle a stress test with different tree operations (Concurrent Mode)', () => {37    let setShowX;38    const A = () => 'a';39    const B = () => 'b';40    const C = () => {41      // We'll be manually flipping this component back and forth in the test.42      // We only do this for a single node in order to verify that DevTools43      // can handle a subtree switching alternates while other subtrees are memoized.44      const [showX, _setShowX] = React.useState(false);45      setShowX = _setShowX;46      return showX ? <X /> : 'c';47    };48    const D = () => 'd';49    const E = () => 'e';50    const X = () => 'x';51    const a = <A key="a" />;52    const b = <B key="b" />;53    const c = <C key="c" />;54    const d = <D key="d" />;55    const e = <E key="e" />;56    function Parent({children}) {57      return children;58    }59    // 1. Render a normal version of [a, b, c, d, e].60    let container = document.createElement('div');61    // $FlowFixMe62    let root = ReactDOM.createRoot(container);63    act(() => root.render(<Parent>{[a, b, c, d, e]}</Parent>));64    expect(store).toMatchInlineSnapshot(65      `66      [root]67        â¾ <Parent>68            <A key="a">69            <B key="b">70            <C key="c">71            <D key="d">72            <E key="e">73    `,74    );75    expect(container.textContent).toMatch('abcde');76    const snapshotForABCDE = print(store);77    // 2. Render a version where <C /> renders an <X /> child instead of 'c'.78    // This is how we'll test an update to a single component.79    act(() => {80      setShowX(true);81    });82    expect(store).toMatchInlineSnapshot(83      `84      [root]85        â¾ <Parent>86            <A key="a">87            <B key="b">88          â¾ <C key="c">89              <X>90            <D key="d">91            <E key="e">92    `,93    );94    expect(container.textContent).toMatch('abxde');95    const snapshotForABXDE = print(store);96    // 3. Verify flipping it back produces the original result.97    act(() => {98      setShowX(false);99    });100    expect(container.textContent).toMatch('abcde');101    expect(print(store)).toBe(snapshotForABCDE);102    // 4. Clean up.103    act(() => root.unmount());104    expect(print(store)).toBe('');105    // Now comes the interesting part.106    // All of these cases are equivalent to [a, b, c, d, e] in output.107    // We'll verify that DevTools produces the same snapshots for them.108    // These cases are picked so that rendering them sequentially in the same109    // container results in a combination of mounts, updates, unmounts, and reorders.110    // prettier-ignore111    const cases = [112      [a, b, c, d, e],113      [[a], b, c, d, e],114      [[a, b], c, d, e],115      [[a, b], c, [d, e]],116      [[a, b], c, [d, '', e]],117      [[a], b, c, d, [e]],118      [a, b, [[c]], d, e],119      [[a, ''], [b], [c], [d], [e]],120      [a, b, [c, [d, ['', e]]]],121      [a, b, c, d, e],122      [<div key="0">{a}</div>, b, c, d, e],123      [<div key="0">{a}{b}</div>, c, d, e],124      [<div key="0">{a}{b}</div>, c, <div key="1">{d}{e}</div>],125      [<div key="1">{a}{b}</div>, c, <div key="0">{d}{e}</div>],126      [<div key="0">{a}{b}</div>, c, <div key="1">{d}{e}</div>],127      [<div key="2">{a}{b}</div>, c, <div key="3">{d}{e}</div>],128      [<span key="0">{a}</span>, b, c, d, [e]],129      [a, b, <span key="0"><span>{c}</span></span>, d, e],130      [<div key="0">{a}</div>, [b], <span key="1">{c}</span>, [d], <div key="2">{e}</div>],131      [a, b, [c, <div key="0">{d}<span>{e}</span></div>], ''],132      [a, [[]], b, c, [d, [[]], e]],133      [[[a, b, c, d], e]],134      [a, b, c, d, e],135    ];136    // 5. Test fresh mount for each case.137    for (let i = 0; i < cases.length; i++) {138      // Ensure fresh mount.139      container = document.createElement('div');140      // $FlowFixMe141      root = ReactDOM.createRoot(container);142      // Verify mounting 'abcde'.143      act(() => root.render(<Parent>{cases[i]}</Parent>));144      expect(container.textContent).toMatch('abcde');145      expect(print(store)).toEqual(snapshotForABCDE);146      // Verify switching to 'abxde'.147      act(() => {148        setShowX(true);149      });150      expect(container.textContent).toMatch('abxde');151      expect(print(store)).toBe(snapshotForABXDE);152      // Verify switching back to 'abcde'.153      act(() => {154        setShowX(false);155      });156      expect(container.textContent).toMatch('abcde');157      expect(print(store)).toBe(snapshotForABCDE);158      // Clean up.159      act(() => root.unmount());160      expect(print(store)).toBe('');161    }162    // 6. Verify *updates* by reusing the container between iterations.163    // There'll be no unmounting until the very end.164    container = document.createElement('div');165    // $FlowFixMe166    root = ReactDOM.createRoot(container);167    for (let i = 0; i < cases.length; i++) {168      // Verify mounting 'abcde'.169      act(() => root.render(<Parent>{cases[i]}</Parent>));170      expect(container.textContent).toMatch('abcde');171      expect(print(store)).toEqual(snapshotForABCDE);172      // Verify switching to 'abxde'.173      act(() => {174        setShowX(true);175      });176      expect(container.textContent).toMatch('abxde');177      expect(print(store)).toBe(snapshotForABXDE);178      // Verify switching back to 'abcde'.179      act(() => {180        setShowX(false);181      });182      expect(container.textContent).toMatch('abcde');183      expect(print(store)).toBe(snapshotForABCDE);184      // Don't unmount. Reuse the container between iterations.185    }186    act(() => root.unmount());187    expect(print(store)).toBe('');188  });189  it('should handle stress test with reordering (Concurrent Mode)', () => {190    const A = () => 'a';191    const B = () => 'b';192    const C = () => 'c';193    const D = () => 'd';194    const E = () => 'e';195    const a = <A key="a" />;196    const b = <B key="b" />;197    const c = <C key="c" />;198    const d = <D key="d" />;199    const e = <E key="e" />;200    // prettier-ignore201    const steps = [202      a,203      b,204      c,205      d,206      e,207      [a],208      [b],209      [c],210      [d],211      [e],212      [a, b],213      [b, a],214      [b, c],215      [c, b],216      [a, c],217      [c, a],218    ];219    const Root = ({children}) => {220      return children;221    };222    // 1. Capture the expected render result.223    const snapshots = [];224    let container = document.createElement('div');225    // $FlowFixMe226    let root = ReactDOM.createRoot(container);227    for (let i = 0; i < steps.length; i++) {228      act(() => root.render(<Root>{steps[i]}</Root>));229      // We snapshot each step once so it doesn't regress.230      snapshots.push(print(store));231      act(() => root.unmount());232      expect(print(store)).toBe('');233    }234    expect(snapshots).toMatchInlineSnapshot(`235      Array [236        "[root]237        â¾ <Root>238            <A key=\\"a\\">",239        "[root]240        â¾ <Root>241            <B key=\\"b\\">",242        "[root]243        â¾ <Root>244            <C key=\\"c\\">",245        "[root]246        â¾ <Root>247            <D key=\\"d\\">",248        "[root]249        â¾ <Root>250            <E key=\\"e\\">",251        "[root]252        â¾ <Root>253            <A key=\\"a\\">",254        "[root]255        â¾ <Root>256            <B key=\\"b\\">",257        "[root]258        â¾ <Root>259            <C key=\\"c\\">",260        "[root]261        â¾ <Root>262            <D key=\\"d\\">",263        "[root]264        â¾ <Root>265            <E key=\\"e\\">",266        "[root]267        â¾ <Root>268            <A key=\\"a\\">269            <B key=\\"b\\">",270        "[root]271        â¾ <Root>272            <B key=\\"b\\">273            <A key=\\"a\\">",274        "[root]275        â¾ <Root>276            <B key=\\"b\\">277            <C key=\\"c\\">",278        "[root]279        â¾ <Root>280            <C key=\\"c\\">281            <B key=\\"b\\">",282        "[root]283        â¾ <Root>284            <A key=\\"a\\">285            <C key=\\"c\\">",286        "[root]287        â¾ <Root>288            <C key=\\"c\\">289            <A key=\\"a\\">",290      ]291    `);292    // 2. Verify that we can update from every step to every other step and back.293    for (let i = 0; i < steps.length; i++) {294      for (let j = 0; j < steps.length; j++) {295        container = document.createElement('div');296        // $FlowFixMe297        root = ReactDOM.createRoot(container);298        act(() => root.render(<Root>{steps[i]}</Root>));299        expect(print(store)).toMatch(snapshots[i]);300        act(() => root.render(<Root>{steps[j]}</Root>));301        expect(print(store)).toMatch(snapshots[j]);302        act(() => root.render(<Root>{steps[i]}</Root>));303        expect(print(store)).toMatch(snapshots[i]);304        act(() => root.unmount());305        expect(print(store)).toBe('');306      }307    }308    // 3. Same test as above, but this time we wrap children in a host component.309    for (let i = 0; i < steps.length; i++) {310      for (let j = 0; j < steps.length; j++) {311        container = document.createElement('div');312        // $FlowFixMe313        root = ReactDOM.createRoot(container);314        act(() =>315          root.render(316            <Root>317              <div>{steps[i]}</div>318            </Root>,319          ),320        );321        expect(print(store)).toMatch(snapshots[i]);322        act(() =>323          root.render(324            <Root>325              <div>{steps[j]}</div>326            </Root>,327          ),328        );329        expect(print(store)).toMatch(snapshots[j]);330        act(() =>331          root.render(332            <Root>333              <div>{steps[i]}</div>334            </Root>,335          ),336        );337        expect(print(store)).toMatch(snapshots[i]);338        act(() => root.unmount());339        expect(print(store)).toBe('');340      }341    }342  });343  it('should handle a stress test for Suspense (Concurrent Mode)', async () => {344    const A = () => 'a';345    const B = () => 'b';346    const C = () => 'c';347    const X = () => 'x';348    const Y = () => 'y';349    const Z = () => 'z';350    const a = <A key="a" />;351    const b = <B key="b" />;352    const c = <C key="c" />;353    const z = <Z key="z" />;354    // prettier-ignore355    const steps = [356      a,357      [a],358      [a, b, c],359      [c, b, a],360      [c, null, a],361      <React.Fragment>{c}{a}</React.Fragment>,362      <div>{c}{a}</div>,363      <div><span>{a}</span>{b}</div>,364      [[a]],365      null,366      b,367      a,368    ];369    const Never = () => {370      throw new Promise(() => {});371    };372    const Root = ({children}) => {373      return children;374    };375    // 1. For each step, check Suspense can render them as initial primary content.376    // This is the only step where we use Jest snapshots.377    const snapshots = [];378    let container = document.createElement('div');379    // $FlowFixMe380    let root = ReactDOM.createRoot(container);381    for (let i = 0; i < steps.length; i++) {382      act(() =>383        root.render(384          <Root>385            <X />386            <React.Suspense fallback={z}>{steps[i]}</React.Suspense>387            <Y />388          </Root>,389        ),390      );391      // We snapshot each step once so it doesn't regress.d392      snapshots.push(print(store));393      act(() => root.unmount());394      expect(print(store)).toBe('');395    }396    expect(snapshots).toMatchInlineSnapshot(`397      Array [398        "[root]399        â¾ <Root>400            <X>401          â¾ <Suspense>402              <A key=\\"a\\">403            <Y>",404        "[root]405        â¾ <Root>406            <X>407          â¾ <Suspense>408              <A key=\\"a\\">409            <Y>",410        "[root]411        â¾ <Root>412            <X>413          â¾ <Suspense>414              <A key=\\"a\\">415              <B key=\\"b\\">416              <C key=\\"c\\">417            <Y>",418        "[root]419        â¾ <Root>420            <X>421          â¾ <Suspense>422              <C key=\\"c\\">423              <B key=\\"b\\">424              <A key=\\"a\\">425            <Y>",426        "[root]427        â¾ <Root>428            <X>429          â¾ <Suspense>430              <C key=\\"c\\">431              <A key=\\"a\\">432            <Y>",433        "[root]434        â¾ <Root>435            <X>436          â¾ <Suspense>437              <C key=\\"c\\">438              <A key=\\"a\\">439            <Y>",440        "[root]441        â¾ <Root>442            <X>443          â¾ <Suspense>444              <C key=\\"c\\">445              <A key=\\"a\\">446            <Y>",447        "[root]448        â¾ <Root>449            <X>450          â¾ <Suspense>451              <A key=\\"a\\">452              <B key=\\"b\\">453            <Y>",454        "[root]455        â¾ <Root>456            <X>457          â¾ <Suspense>458              <A key=\\"a\\">459            <Y>",460        "[root]461        â¾ <Root>462            <X>463            <Suspense>464            <Y>",465        "[root]466        â¾ <Root>467            <X>468          â¾ <Suspense>469              <B key=\\"b\\">470            <Y>",471        "[root]472        â¾ <Root>473            <X>474          â¾ <Suspense>475              <A key=\\"a\\">476            <Y>",477      ]478    `);479    // 2. Verify check Suspense can render same steps as initial fallback content.480    for (let i = 0; i < steps.length; i++) {481      act(() =>482        root.render(483          <Root>484            <X />485            <React.Suspense fallback={steps[i]}>486              <Z />487              <Never />488              <Z />489            </React.Suspense>490            <Y />491          </Root>,492        ),493      );494      expect(print(store)).toEqual(snapshots[i]);495      act(() => root.unmount());496      expect(print(store)).toBe('');497    }498    // 3. Verify we can update from each step to each step in primary mode.499    for (let i = 0; i < steps.length; i++) {500      for (let j = 0; j < steps.length; j++) {501        // Always start with a fresh container and steps[i].502        container = document.createElement('div');503        // $FlowFixMe504        root = ReactDOM.createRoot(container);505        act(() =>506          root.render(507            <Root>508              <X />509              <React.Suspense fallback={z}>{steps[i]}</React.Suspense>510              <Y />511            </Root>,512          ),513        );514        expect(print(store)).toEqual(snapshots[i]);515        // Re-render with steps[j].516        act(() =>517          root.render(518            <Root>519              <X />520              <React.Suspense fallback={z}>{steps[j]}</React.Suspense>521              <Y />522            </Root>,523          ),524        );525        // Verify the successful transition to steps[j].526        expect(print(store)).toEqual(snapshots[j]);527        // Check that we can transition back again.528        act(() =>529          root.render(530            <Root>531              <X />532              <React.Suspense fallback={z}>{steps[i]}</React.Suspense>533              <Y />534            </Root>,535          ),536        );537        expect(print(store)).toEqual(snapshots[i]);538        // Clean up after every iteration.539        act(() => root.unmount());540        expect(print(store)).toBe('');541      }542    }543    // 4. Verify we can update from each step to each step in fallback mode.544    for (let i = 0; i < steps.length; i++) {545      for (let j = 0; j < steps.length; j++) {546        // Always start with a fresh container and steps[i].547        container = document.createElement('div');548        // $FlowFixMe549        root = ReactDOM.createRoot(container);550        act(() =>551          root.render(552            <Root>553              <X />554              <React.Suspense fallback={steps[i]}>555                <Z />556                <Never />557                <Z />558              </React.Suspense>559              <Y />560            </Root>,561          ),562        );563        expect(print(store)).toEqual(snapshots[i]);564        // Re-render with steps[j].565        act(() =>566          root.render(567            <Root>568              <X />569              <React.Suspense fallback={steps[j]}>570                <Z />571                <Never />572                <Z />573              </React.Suspense>574              <Y />575            </Root>,576          ),577        );578        // Verify the successful transition to steps[j].579        expect(print(store)).toEqual(snapshots[j]);580        // Check that we can transition back again.581        act(() =>582          root.render(583            <Root>584              <X />585              <React.Suspense fallback={steps[i]}>586                <Z />587                <Never />588                <Z />589              </React.Suspense>590              <Y />591            </Root>,592          ),593        );594        expect(print(store)).toEqual(snapshots[i]);595        // Clean up after every iteration.596        act(() => root.unmount());597        expect(print(store)).toBe('');598      }599    }600    // 5. Verify we can update from each step to each step when moving primary -> fallback.601    for (let i = 0; i < steps.length; i++) {602      for (let j = 0; j < steps.length; j++) {603        // Always start with a fresh container and steps[i].604        container = document.createElement('div');605        // $FlowFixMe606        root = ReactDOM.createRoot(container);607        act(() =>608          root.render(609            <Root>610              <X />611              <React.Suspense fallback={z}>{steps[i]}</React.Suspense>612              <Y />613            </Root>,614          ),615        );616        expect(print(store)).toEqual(snapshots[i]);617        // Re-render with steps[j].618        act(() =>619          root.render(620            <Root>621              <X />622              <React.Suspense fallback={steps[j]}>623                <Z />624                <Never />625                <Z />626              </React.Suspense>627              <Y />628            </Root>,629          ),630        );631        // Verify the successful transition to steps[j].632        expect(print(store)).toEqual(snapshots[j]);633        // Check that we can transition back again.634        act(() =>635          root.render(636            <Root>637              <X />638              <React.Suspense fallback={z}>{steps[i]}</React.Suspense>639              <Y />640            </Root>,641          ),642        );643        expect(print(store)).toEqual(snapshots[i]);644        // Clean up after every iteration.645        act(() => root.unmount());646        expect(print(store)).toBe('');647      }648    }649    // 6. Verify we can update from each step to each step when moving fallback -> primary.650    for (let i = 0; i < steps.length; i++) {651      for (let j = 0; j < steps.length; j++) {652        // Always start with a fresh container and steps[i].653        container = document.createElement('div');654        // $FlowFixMe655        root = ReactDOM.createRoot(container);656        act(() =>657          root.render(658            <Root>659              <X />660              <React.Suspense fallback={steps[i]}>661                <Z />662                <Never />663                <Z />664              </React.Suspense>665              <Y />666            </Root>,667          ),668        );669        expect(print(store)).toEqual(snapshots[i]);670        // Re-render with steps[j].671        act(() =>672          root.render(673            <Root>674              <X />675              <React.Suspense fallback={z}>{steps[j]}</React.Suspense>676              <Y />677            </Root>,678          ),679        );680        // Verify the successful transition to steps[j].681        expect(print(store)).toEqual(snapshots[j]);682        // Check that we can transition back again.683        act(() =>684          root.render(685            <Root>686              <X />687              <React.Suspense fallback={steps[i]}>688                <Z />689                <Never />690                <Z />691              </React.Suspense>692              <Y />693            </Root>,694          ),695        );696        expect(print(store)).toEqual(snapshots[i]);697        // Clean up after every iteration.698        act(() => root.unmount());699        expect(print(store)).toBe('');700      }701    }702    // 7. Verify we can update from each step to each step when toggling Suspense.703    for (let i = 0; i < steps.length; i++) {704      for (let j = 0; j < steps.length; j++) {705        // Always start with a fresh container and steps[i].706        container = document.createElement('div');707        // $FlowFixMe708        root = ReactDOM.createRoot(container);709        act(() =>710          root.render(711            <Root>712              <X />713              <React.Suspense fallback={steps[j]}>{steps[i]}</React.Suspense>714              <Y />715            </Root>,716          ),717        );718        // We get ID from the index in the tree above:719        // Root, X, Suspense, ...720        //          ^ (index is 2)721        const suspenseID = store.getElementIDAtIndex(2);722        // Force fallback.723        expect(print(store)).toEqual(snapshots[i]);724        await actAsync(async () => {725          bridge.send('overrideSuspense', {726            id: suspenseID,727            rendererID: store.getRendererIDForElement(suspenseID),728            forceFallback: true,729          });730        });731        expect(print(store)).toEqual(snapshots[j]);732        // Stop forcing fallback.733        await actAsync(async () => {734          bridge.send('overrideSuspense', {735            id: suspenseID,736            rendererID: store.getRendererIDForElement(suspenseID),737            forceFallback: false,738          });739        });740        expect(print(store)).toEqual(snapshots[i]);741        // Trigger actual fallback.742        act(() =>743          root.render(744            <Root>745              <X />746              <React.Suspense fallback={steps[j]}>747                <Z />748                <Never />749                <Z />750              </React.Suspense>751              <Y />752            </Root>,753          ),754        );755        expect(print(store)).toEqual(snapshots[j]);756        // Force fallback while we're in fallback mode.757        act(() => {758          bridge.send('overrideSuspense', {759            id: suspenseID,760            rendererID: store.getRendererIDForElement(suspenseID),761            forceFallback: true,762          });763        });764        // Keep seeing fallback content.765        expect(print(store)).toEqual(snapshots[j]);766        // Switch to primary mode.767        act(() =>768          root.render(769            <Root>770              <X />771              <React.Suspense fallback={steps[j]}>{steps[i]}</React.Suspense>772              <Y />773            </Root>,774          ),775        );776        // Fallback is still forced though.777        expect(print(store)).toEqual(snapshots[j]);778        // Stop forcing fallback. This reverts to primary content.779        await actAsync(async () => {780          bridge.send('overrideSuspense', {781            id: suspenseID,782            rendererID: store.getRendererIDForElement(suspenseID),783            forceFallback: false,784          });785        });786        // Now we see primary content.787        expect(print(store)).toEqual(snapshots[i]);788        // Clean up after every iteration.789        await actAsync(async () => root.unmount());790        expect(print(store)).toBe('');791      }792    }793  });794  it('should handle a stress test for Suspense without type change (Concurrent Mode)', async () => {795    const A = () => 'a';796    const B = () => 'b';797    const C = () => 'c';798    const X = () => 'x';799    const Y = () => 'y';800    const Z = () => 'z';801    const a = <A key="a" />;802    const b = <B key="b" />;803    const c = <C key="c" />;804    const z = <Z key="z" />;805    // prettier-ignore806    const steps = [807      a,808      [a],809      [a, b, c],810      [c, b, a],811      [c, null, a],812      <React.Fragment>{c}{a}</React.Fragment>,813      <div>{c}{a}</div>,814      <div><span>{a}</span>{b}</div>,815      [[a]],816      null,817      b,818      a,819    ];820    const Never = () => {821      throw new Promise(() => {});822    };823    const MaybeSuspend = ({children, suspend}) => {824      if (suspend) {825        return (826          <div>827            {children}828            <Never />829            <X />830          </div>831        );832      }833      return (834        <div>835          {children}836          <Z />837        </div>838      );839    };840    const Root = ({children}) => {841      return children;842    };843    // 1. For each step, check Suspense can render them as initial primary content.844    // This is the only step where we use Jest snapshots.845    const snapshots = [];846    let container = document.createElement('div');847    // $FlowFixMe848    let root = ReactDOM.createRoot(container);849    for (let i = 0; i < steps.length; i++) {850      act(() =>851        root.render(852          <Root>853            <X />854            <React.Suspense fallback={z}>855              <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>856            </React.Suspense>857            <Y />858          </Root>,859        ),860      );861      // We snapshot each step once so it doesn't regress.862      snapshots.push(print(store));863      act(() => root.unmount());864      expect(print(store)).toBe('');865    }866    // 2. Verify check Suspense can render same steps as initial fallback content.867    // We don't actually assert here because the tree includes <MaybeSuspend>868    // which is different from the snapshots above. So we take more snapshots.869    const fallbackSnapshots = [];870    for (let i = 0; i < steps.length; i++) {871      act(() =>872        root.render(873          <Root>874            <X />875            <React.Suspense fallback={steps[i]}>876              <Z />877              <MaybeSuspend suspend={true}>{steps[i]}</MaybeSuspend>878              <Z />879            </React.Suspense>880            <Y />881          </Root>,882        ),883      );884      // We snapshot each step once so it doesn't regress.885      fallbackSnapshots.push(print(store));886      act(() => root.unmount());887      expect(print(store)).toBe('');888    }889    expect(snapshots).toMatchInlineSnapshot(`890      Array [891        "[root]892        â¾ <Root>893            <X>894          â¾ <Suspense>895            â¾ <MaybeSuspend>896                <A key=\\"a\\">897                <Z>898            <Y>",899        "[root]900        â¾ <Root>901            <X>902          â¾ <Suspense>903            â¾ <MaybeSuspend>904                <A key=\\"a\\">905                <Z>906            <Y>",907        "[root]908        â¾ <Root>909            <X>910          â¾ <Suspense>911            â¾ <MaybeSuspend>912                <A key=\\"a\\">913                <B key=\\"b\\">914                <C key=\\"c\\">915                <Z>916            <Y>",917        "[root]918        â¾ <Root>919            <X>920          â¾ <Suspense>921            â¾ <MaybeSuspend>922                <C key=\\"c\\">923                <B key=\\"b\\">924                <A key=\\"a\\">925                <Z>926            <Y>",927        "[root]928        â¾ <Root>929            <X>930          â¾ <Suspense>931            â¾ <MaybeSuspend>932                <C key=\\"c\\">933                <A key=\\"a\\">934                <Z>935            <Y>",936        "[root]937        â¾ <Root>938            <X>939          â¾ <Suspense>940            â¾ <MaybeSuspend>941                <C key=\\"c\\">942                <A key=\\"a\\">943                <Z>944            <Y>",945        "[root]946        â¾ <Root>947            <X>948          â¾ <Suspense>949            â¾ <MaybeSuspend>950                <C key=\\"c\\">951                <A key=\\"a\\">952                <Z>953            <Y>",954        "[root]955        â¾ <Root>956            <X>957          â¾ <Suspense>958            â¾ <MaybeSuspend>959                <A key=\\"a\\">960                <B key=\\"b\\">961                <Z>962            <Y>",963        "[root]964        â¾ <Root>965            <X>966          â¾ <Suspense>967            â¾ <MaybeSuspend>968                <A key=\\"a\\">969                <Z>970            <Y>",971        "[root]972        â¾ <Root>973            <X>974          â¾ <Suspense>975            â¾ <MaybeSuspend>976                <Z>977            <Y>",978        "[root]979        â¾ <Root>980            <X>981          â¾ <Suspense>982            â¾ <MaybeSuspend>983                <B key=\\"b\\">984                <Z>985            <Y>",986        "[root]987        â¾ <Root>988            <X>989          â¾ <Suspense>990            â¾ <MaybeSuspend>991                <A key=\\"a\\">992                <Z>993            <Y>",994      ]995    `);996    // 3. Verify we can update from each step to each step in primary mode.997    for (let i = 0; i < steps.length; i++) {998      for (let j = 0; j < steps.length; j++) {999        // Always start with a fresh container and steps[i].1000        container = document.createElement('div');1001        // $FlowFixMe1002        root = ReactDOM.createRoot(container);1003        act(() =>1004          root.render(1005            <Root>1006              <X />1007              <React.Suspense fallback={z}>1008                <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>1009              </React.Suspense>1010              <Y />1011            </Root>,1012          ),1013        );1014        expect(print(store)).toEqual(snapshots[i]);1015        // Re-render with steps[j].1016        act(() =>1017          root.render(1018            <Root>1019              <X />1020              <React.Suspense fallback={z}>1021                <MaybeSuspend suspend={false}>{steps[j]}</MaybeSuspend>1022              </React.Suspense>1023              <Y />1024            </Root>,1025          ),1026        );1027        // Verify the successful transition to steps[j].1028        expect(print(store)).toEqual(snapshots[j]);1029        // Check that we can transition back again.1030        act(() =>1031          root.render(1032            <Root>1033              <X />1034              <React.Suspense fallback={z}>1035                <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>1036              </React.Suspense>1037              <Y />1038            </Root>,1039          ),1040        );1041        expect(print(store)).toEqual(snapshots[i]);1042        // Clean up after every iteration.1043        act(() => root.unmount());1044        expect(print(store)).toBe('');1045      }1046    }1047    // 4. Verify we can update from each step to each step in fallback mode.1048    for (let i = 0; i < steps.length; i++) {1049      for (let j = 0; j < steps.length; j++) {1050        // Always start with a fresh container and steps[i].1051        container = document.createElement('div');1052        // $FlowFixMe1053        root = ReactDOM.createRoot(container);1054        act(() =>1055          root.render(1056            <Root>1057              <X />1058              <React.Suspense fallback={steps[i]}>1059                <Z />1060                <MaybeSuspend suspend={true}>1061                  <X />1062                  <Y />1063                </MaybeSuspend>1064                <Z />1065              </React.Suspense>1066              <Y />1067            </Root>,1068          ),1069        );1070        expect(print(store)).toEqual(fallbackSnapshots[i]);1071        // Re-render with steps[j].1072        act(() =>1073          root.render(1074            <Root>1075              <X />1076              <React.Suspense fallback={steps[j]}>1077                <Z />1078                <MaybeSuspend suspend={true}>1079                  <Y />1080                  <X />1081                </MaybeSuspend>1082                <Z />1083              </React.Suspense>1084              <Y />1085            </Root>,1086          ),1087        );1088        // Verify the successful transition to steps[j].1089        expect(print(store)).toEqual(fallbackSnapshots[j]);1090        // Check that we can transition back again.1091        act(() =>1092          root.render(1093            <Root>1094              <X />1095              <React.Suspense fallback={steps[i]}>1096                <Z />1097                <MaybeSuspend suspend={true}>1098                  <X />1099                  <Y />1100                </MaybeSuspend>1101                <Z />1102              </React.Suspense>1103              <Y />1104            </Root>,1105          ),1106        );1107        expect(print(store)).toEqual(fallbackSnapshots[i]);1108        // Clean up after every iteration.1109        act(() => root.unmount());1110        expect(print(store)).toBe('');1111      }1112    }1113    // 5. Verify we can update from each step to each step when moving primary -> fallback.1114    for (let i = 0; i < steps.length; i++) {1115      for (let j = 0; j < steps.length; j++) {1116        // Always start with a fresh container and steps[i].1117        container = document.createElement('div');1118        // $FlowFixMe1119        root = ReactDOM.createRoot(container);1120        act(() =>1121          root.render(1122            <Root>1123              <X />1124              <React.Suspense fallback={z}>1125                <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>1126              </React.Suspense>1127              <Y />1128            </Root>,1129          ),1130        );1131        expect(print(store)).toEqual(snapshots[i]);1132        // Re-render with steps[j].1133        act(() =>1134          root.render(1135            <Root>1136              <X />1137              <React.Suspense fallback={steps[j]}>1138                <MaybeSuspend suspend={true}>{steps[i]}</MaybeSuspend>1139              </React.Suspense>1140              <Y />1141            </Root>,1142          ),1143        );1144        // Verify the successful transition to steps[j].1145        expect(print(store)).toEqual(fallbackSnapshots[j]);1146        // Check that we can transition back again.1147        act(() =>1148          root.render(1149            <Root>1150              <X />1151              <React.Suspense fallback={z}>1152                <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>1153              </React.Suspense>1154              <Y />1155            </Root>,1156          ),1157        );1158        expect(print(store)).toEqual(snapshots[i]);1159        // Clean up after every iteration.1160        act(() => root.unmount());1161        expect(print(store)).toBe('');1162      }1163    }1164    // 6. Verify we can update from each step to each step when moving fallback -> primary.1165    for (let i = 0; i < steps.length; i++) {1166      for (let j = 0; j < steps.length; j++) {1167        // Always start with a fresh container and steps[i].1168        container = document.createElement('div');1169        // $FlowFixMe1170        root = ReactDOM.createRoot(container);1171        act(() =>1172          root.render(1173            <Root>1174              <X />1175              <React.Suspense fallback={steps[i]}>1176                <MaybeSuspend suspend={true}>{steps[j]}</MaybeSuspend>1177              </React.Suspense>1178              <Y />1179            </Root>,1180          ),1181        );1182        expect(print(store)).toEqual(fallbackSnapshots[i]);1183        // Re-render with steps[j].1184        act(() =>1185          root.render(1186            <Root>1187              <X />1188              <React.Suspense fallback={steps[i]}>1189                <MaybeSuspend suspend={false}>{steps[j]}</MaybeSuspend>1190              </React.Suspense>1191              <Y />1192            </Root>,1193          ),1194        );1195        // Verify the successful transition to steps[j].1196        expect(print(store)).toEqual(snapshots[j]);1197        // Check that we can transition back again.1198        act(() =>1199          root.render(1200            <Root>1201              <X />1202              <React.Suspense fallback={steps[i]}>1203                <MaybeSuspend suspend={true}>{steps[j]}</MaybeSuspend>1204              </React.Suspense>1205              <Y />1206            </Root>,1207          ),1208        );1209        expect(print(store)).toEqual(fallbackSnapshots[i]);1210        // Clean up after every iteration.1211        act(() => root.unmount());1212        expect(print(store)).toBe('');1213      }1214    }1215    // 7. Verify we can update from each step to each step when toggling Suspense.1216    for (let i = 0; i < steps.length; i++) {1217      for (let j = 0; j < steps.length; j++) {1218        // Always start with a fresh container and steps[i].1219        container = document.createElement('div');1220        // $FlowFixMe1221        root = ReactDOM.createRoot(container);1222        act(() =>1223          root.render(1224            <Root>1225              <X />1226              <React.Suspense fallback={steps[j]}>1227                <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>1228              </React.Suspense>1229              <Y />1230            </Root>,1231          ),1232        );1233        // We get ID from the index in the tree above:1234        // Root, X, Suspense, ...1235        //          ^ (index is 2)1236        const suspenseID = store.getElementIDAtIndex(2);1237        // Force fallback.1238        expect(print(store)).toEqual(snapshots[i]);1239        await actAsync(async () => {1240          bridge.send('overrideSuspense', {1241            id: suspenseID,1242            rendererID: store.getRendererIDForElement(suspenseID),1243            forceFallback: true,1244          });1245        });1246        expect(print(store)).toEqual(fallbackSnapshots[j]);1247        // Stop forcing fallback.1248        await actAsync(async () => {1249          bridge.send('overrideSuspense', {1250            id: suspenseID,1251            rendererID: store.getRendererIDForElement(suspenseID),1252            forceFallback: false,1253          });1254        });1255        expect(print(store)).toEqual(snapshots[i]);1256        // Trigger actual fallback.1257        act(() =>1258          root.render(1259            <Root>1260              <X />1261              <React.Suspense fallback={steps[j]}>1262                <MaybeSuspend suspend={true}>{steps[i]}</MaybeSuspend>1263              </React.Suspense>1264              <Y />1265            </Root>,1266          ),1267        );1268        expect(print(store)).toEqual(fallbackSnapshots[j]);1269        // Force fallback while we're in fallback mode.1270        act(() => {1271          bridge.send('overrideSuspense', {1272            id: suspenseID,1273            rendererID: store.getRendererIDForElement(suspenseID),1274            forceFallback: true,1275          });1276        });1277        // Keep seeing fallback content.1278        expect(print(store)).toEqual(fallbackSnapshots[j]);1279        // Switch to primary mode.1280        act(() =>1281          root.render(1282            <Root>1283              <X />1284              <React.Suspense fallback={steps[j]}>1285                <MaybeSuspend suspend={false}>{steps[i]}</MaybeSuspend>1286              </React.Suspense>1287              <Y />1288            </Root>,1289          ),1290        );1291        // Fallback is still forced though.1292        expect(print(store)).toEqual(fallbackSnapshots[j]);1293        // Stop forcing fallback. This reverts to primary content.1294        await actAsync(async () => {1295          bridge.send('overrideSuspense', {1296            id: suspenseID,1297            rendererID: store.getRendererIDForElement(suspenseID),1298            forceFallback: false,1299          });1300        });1301        // Now we see primary content.1302        expect(print(store)).toEqual(snapshots[i]);1303        // Clean up after every iteration.1304        act(() => root.unmount());1305        expect(print(store)).toBe('');1306      }1307    }1308  });...DependencyGraph-test.js
Source:DependencyGraph-test.js  
1'use strict';2jest3  .dontMock('../index')4  .dontMock('q')5  .dontMock('path')6  .dontMock('absolute-path')7  .dontMock('../docblock')8  .setMock('../../../ModuleDescriptor', function(data) {return data;});9describe('DependencyGraph', function() {10  var DependencyGraph;11  var fileWatcher;12  var fs;13  beforeEach(function() {14    fs = require('fs');15    DependencyGraph = require('../index');16    fileWatcher = {17      on: function() {18        return this;19      }20    };21  });22  describe('getOrderedDependencies', function() {23    pit('should get dependencies', function() {24      var root = '/root';25      fs.__setMockFilesystem({26        'root': {27          'index.js': [28            '/**',29            ' * @providesModule index',30            ' */',31            'require("a")'32          ].join('\n'),33          'a.js': [34            '/**',35            ' * @providesModule a',36            ' */',37          ].join('\n'),38        }39      });40      var dgraph = new DependencyGraph({41        roots: [root],42        fileWatcher: fileWatcher43      });44      return dgraph.load().then(function() {45        expect(dgraph.getOrderedDependencies('/root/index.js'))46          .toEqual([47            {id: 'index', path: '/root/index.js', dependencies: ['a']},48            {id: 'a', path: '/root/a.js', dependencies: []},49          ]);50      });51    });52    pit('should get dependencies', function() {53      var root = '/root';54      fs.__setMockFilesystem({55        'root': {56          'index.js': [57            '/**',58            ' * @providesModule index',59            ' */',60            'require("image!a")'61          ].join('\n'),62          'imgs': {63            'a.png': ''64          },65        }66      });67      var dgraph = new DependencyGraph({68        roots: [root],69        fileWatcher: fileWatcher,70        assetRoots: ['/root/imgs']71      });72      return dgraph.load().then(function() {73        expect(dgraph.getOrderedDependencies('/root/index.js'))74          .toEqual([75            {id: 'index', path: '/root/index.js', dependencies: ['image!a']},76            {  id: 'image!a',77               path: '/root/imgs/a.png',78               dependencies: [],79               isAsset: true80            },81          ]);82      });83    });84    pit('should get recursive dependencies', function() {85      var root = '/root';86      fs.__setMockFilesystem({87        'root': {88          'index.js': [89            '/**',90            ' * @providesModule index',91            ' */',92            'require("a")',93          ].join('\n'),94          'a.js': [95            '/**',96            ' * @providesModule a',97            ' */',98            'require("index")',99          ].join('\n'),100        }101      });102      var dgraph = new DependencyGraph({103        roots: [root],104        fileWatcher: fileWatcher105      });106      return dgraph.load().then(function() {107        expect(dgraph.getOrderedDependencies('/root/index.js'))108          .toEqual([109            {id: 'index', path: '/root/index.js', dependencies: ['a']},110            {id: 'a', path: '/root/a.js', dependencies: ['index']},111          ]);112      });113    });114    pit('should work with packages', function() {115      var root = '/root';116      fs.__setMockFilesystem({117        'root': {118          'index.js': [119            '/**',120            ' * @providesModule index',121            ' */',122            'require("aPackage")',123          ].join('\n'),124          'aPackage': {125            'package.json': JSON.stringify({126              name: 'aPackage',127              main: 'main.js'128            }),129            'main.js': 'lol'130          }131        }132      });133      var dgraph = new DependencyGraph({134        roots: [root],135        fileWatcher: fileWatcher136      });137      return dgraph.load().then(function() {138        expect(dgraph.getOrderedDependencies('/root/index.js'))139          .toEqual([140            {id: 'index', path: '/root/index.js', dependencies: ['aPackage']},141            { id: 'aPackage/main',142              path: '/root/aPackage/main.js',143              dependencies: []144            },145          ]);146      });147    });148    pit('should ignore malformed packages', function() {149      var root = '/root';150      fs.__setMockFilesystem({151        'root': {152          'index.js': [153            '/**',154            ' * @providesModule index',155            ' */',156            'require("aPackage")',157          ].join('\n'),158          'aPackage': {159            'package.json': 'lol',160            'main.js': 'lol'161          }162        }163      });164      var dgraph = new DependencyGraph({165        roots: [root],166        fileWatcher: fileWatcher167      });168      return dgraph.load().then(function() {169        expect(dgraph.getOrderedDependencies('/root/index.js'))170          .toEqual([171            {id: 'index', path: '/root/index.js', dependencies: ['aPackage']},172          ]);173      });174    });175    pit('can have multiple modules with the same name', function() {176      var root = '/root';177      fs.__setMockFilesystem({178        'root': {179          'index.js': [180            '/**',181            ' * @providesModule index',182            ' */',183            'require("b")',184          ].join('\n'),185          'b.js': [186            '/**',187            ' * @providesModule b',188            ' */',189          ].join('\n'),190          'c.js': [191            '/**',192            ' * @providesModule c',193            ' */',194          ].join('\n'),195          'somedir': {196            'somefile.js': [197              '/**',198              ' * @providesModule index',199              ' */',200              'require("c")',201            ].join('\n')202          }203        }204      });205      var dgraph = new DependencyGraph({206        roots: [root],207        fileWatcher: fileWatcher208      });209      return dgraph.load().then(function() {210        expect(dgraph.getOrderedDependencies('/root/somedir/somefile.js'))211          .toEqual([212            { id: 'index',213              path: '/root/somedir/somefile.js',214              dependencies: ['c']215            },216            { id: 'c',217              path: '/root/c.js',218              dependencies: []219            },220          ]);221      });222    });223    pit('providesModule wins when conflict with package', function() {224      var root = '/root';225      fs.__setMockFilesystem({226        'root': {227          'index.js': [228            '/**',229            ' * @providesModule index',230            ' */',231            'require("aPackage")',232          ].join('\n'),233          'b.js': [234            '/**',235            ' * @providesModule aPackage',236            ' */',237          ].join('\n'),238          'aPackage': {239            'package.json': JSON.stringify({240              name: 'aPackage',241              main: 'main.js'242            }),243            'main.js': 'lol'244          }245        }246      });247      var dgraph = new DependencyGraph({248        roots: [root],249        fileWatcher: fileWatcher250      });251      return dgraph.load().then(function() {252        expect(dgraph.getOrderedDependencies('/root/index.js'))253          .toEqual([254            { id: 'index',255              path: '/root/index.js',256              dependencies: ['aPackage']257            },258            { id: 'aPackage',259              path: '/root/b.js',260              dependencies: []261            },262          ]);263      });264    });265    pit('should be forgiving with missing requires', function() {266      var root = '/root';267      fs.__setMockFilesystem({268        'root': {269          'index.js': [270            '/**',271            ' * @providesModule index',272            ' */',273            'require("lolomg")',274          ].join('\n')275        }276      });277      var dgraph = new DependencyGraph({278        roots: [root],279        fileWatcher: fileWatcher280      });281      return dgraph.load().then(function() {282        expect(dgraph.getOrderedDependencies('/root/index.js'))283          .toEqual([284            { id: 'index',285              path: '/root/index.js',286              dependencies: ['lolomg']287            }288          ]);289      });290    });291    pit('should work with packages with subdirs', function() {292      var root = '/root';293      fs.__setMockFilesystem({294        'root': {295          'index.js': [296            '/**',297            ' * @providesModule index',298            ' */',299            'require("aPackage/subdir/lolynot")',300          ].join('\n'),301          'aPackage': {302            'package.json': JSON.stringify({303              name: 'aPackage',304              main: 'main.js'305            }),306            'main.js': 'lol',307            'subdir': {308              'lolynot.js': 'lolynot'309            }310          }311        }312      });313      var dgraph = new DependencyGraph({314        roots: [root],315        fileWatcher: fileWatcher316      });317      return dgraph.load().then(function() {318        expect(dgraph.getOrderedDependencies('/root/index.js'))319          .toEqual([320            { id: 'index',321              path: '/root/index.js',322              dependencies: ['aPackage/subdir/lolynot']323            },324            { id: 'aPackage/subdir/lolynot',325              path: '/root/aPackage/subdir/lolynot.js',326              dependencies: []327            },328          ]);329      });330    });331    pit('should work with packages with symlinked subdirs', function() {332      var root = '/root';333      fs.__setMockFilesystem({334        'symlinkedPackage': {335          'package.json': JSON.stringify({336            name: 'aPackage',337            main: 'main.js'338          }),339          'main.js': 'lol',340          'subdir': {341            'lolynot.js': 'lolynot'342          }343        },344        'root': {345          'index.js': [346            '/**',347            ' * @providesModule index',348            ' */',349            'require("aPackage/subdir/lolynot")',350          ].join('\n'),351          'aPackage': { SYMLINK: '/symlinkedPackage' },352        }353      });354      var dgraph = new DependencyGraph({355        roots: [root],356        fileWatcher: fileWatcher357      });358      return dgraph.load().then(function() {359        expect(dgraph.getOrderedDependencies('/root/index.js'))360          .toEqual([361            { id: 'index',362              path: '/root/index.js',363              dependencies: ['aPackage/subdir/lolynot']364            },365            { id: 'aPackage/subdir/lolynot',366              path: '/symlinkedPackage/subdir/lolynot.js',367              dependencies: []368            },369          ]);370      });371    });372    pit('should work with relative modules in packages', function() {373      var root = '/root';374      fs.__setMockFilesystem({375        'root': {376          'index.js': [377            '/**',378            ' * @providesModule index',379            ' */',380            'require("aPackage")',381          ].join('\n'),382          'aPackage': {383            'package.json': JSON.stringify({384              name: 'aPackage',385              main: 'main.js'386            }),387            'main.js': 'require("./subdir/lolynot")',388            'subdir': {389              'lolynot.js': 'require("../other")'390            },391            'other.js': 'some code'392          }393        }394      });395      var dgraph = new DependencyGraph({396        roots: [root],397        fileWatcher: fileWatcher398      });399      return dgraph.load().then(function() {400        expect(dgraph.getOrderedDependencies('/root/index.js'))401          .toEqual([402            { id: 'index',403              path: '/root/index.js',404              dependencies: ['aPackage']405            },406            { id: 'aPackage/main',407              path: '/root/aPackage/main.js',408              dependencies: ['./subdir/lolynot']409            },410            { id: 'aPackage/subdir/lolynot',411              path: '/root/aPackage/subdir/lolynot.js',412              dependencies: ['../other']413            },414            { id: 'aPackage/other',415              path: '/root/aPackage/other.js',416              dependencies: []417            },418          ]);419      });420    });421  });422  describe('file watch updating', function() {423    var triggerFileChange;424    beforeEach(function() {425      fileWatcher = {426        on: function(eventType, callback) {427          if (eventType !== 'all') {428            throw new Error('Can only handle "all" event in watcher.');429          }430          triggerFileChange = callback;431          return this;432        }433      };434    });435    pit('updates module dependencies', function() {436      var root = '/root';437      var filesystem = fs.__setMockFilesystem({438        'root': {439          'index.js': [440            '/**',441            ' * @providesModule index',442            ' */',443            'require("aPackage")',444            'require("foo")'445          ].join('\n'),446          'foo': [447            '/**',448            ' * @providesModule foo',449            ' */',450            'require("aPackage")'451          ].join('\n'),452          'aPackage': {453            'package.json': JSON.stringify({454              name: 'aPackage',455              main: 'main.js'456            }),457            'main.js': 'main',458          }459        }460      });461      var dgraph = new DependencyGraph({462        roots: [root],463        fileWatcher: fileWatcher464      });465      return dgraph.load().then(function() {466        filesystem.root['index.js'] =467          filesystem.root['index.js'].replace('require("foo")', '');468        triggerFileChange('change', 'index.js', root);469        return dgraph.load().then(function() {470          expect(dgraph.getOrderedDependencies('/root/index.js'))471            .toEqual([472            { id: 'index',473              path: '/root/index.js',474              dependencies: ['aPackage']475            },476            { id: 'aPackage/main',477              path: '/root/aPackage/main.js',478              dependencies: []479            },480          ]);481        });482      });483    });484    pit('updates module dependencies on file change', function() {485      var root = '/root';486      var filesystem = fs.__setMockFilesystem({487        'root': {488          'index.js': [489            '/**',490            ' * @providesModule index',491            ' */',492            'require("aPackage")',493            'require("foo")'494          ].join('\n'),495          'foo.js': [496            '/**',497            ' * @providesModule foo',498            ' */',499            'require("aPackage")'500          ].join('\n'),501          'aPackage': {502            'package.json': JSON.stringify({503              name: 'aPackage',504              main: 'main.js'505            }),506            'main.js': 'main',507          }508        }509      });510      var dgraph = new DependencyGraph({511        roots: [root],512        fileWatcher: fileWatcher513      });514      return dgraph.load().then(function() {515        filesystem.root['index.js'] =516          filesystem.root['index.js'].replace('require("foo")', '');517        triggerFileChange('change', 'index.js', root);518        return dgraph.load().then(function() {519          expect(dgraph.getOrderedDependencies('/root/index.js'))520            .toEqual([521            { id: 'index',522              path: '/root/index.js',523              dependencies: ['aPackage']524            },525            { id: 'aPackage/main',526              path: '/root/aPackage/main.js',527              dependencies: []528            },529          ]);530        });531      });532    });533    pit('updates module dependencies on file delete', function() {534      var root = '/root';535      var filesystem = fs.__setMockFilesystem({536        'root': {537          'index.js': [538            '/**',539            ' * @providesModule index',540            ' */',541            'require("aPackage")',542            'require("foo")'543          ].join('\n'),544          'foo.js': [545            '/**',546            ' * @providesModule foo',547            ' */',548            'require("aPackage")'549          ].join('\n'),550          'aPackage': {551            'package.json': JSON.stringify({552              name: 'aPackage',553              main: 'main.js'554            }),555            'main.js': 'main',556          }557        }558      });559      var dgraph = new DependencyGraph({560        roots: [root],561        fileWatcher: fileWatcher562      });563      return dgraph.load().then(function() {564        delete filesystem.root.foo;565        triggerFileChange('delete', 'foo.js', root);566        return dgraph.load().then(function() {567          expect(dgraph.getOrderedDependencies('/root/index.js'))568            .toEqual([569            { id: 'index',570              path: '/root/index.js',571              dependencies: ['aPackage', 'foo']572            },573            { id: 'aPackage/main',574              path: '/root/aPackage/main.js',575              dependencies: []576            },577          ]);578        });579      });580    });581    pit('updates module dependencies on file add', function() {582      var root = '/root';583      var filesystem = fs.__setMockFilesystem({584        'root': {585          'index.js': [586            '/**',587            ' * @providesModule index',588            ' */',589            'require("aPackage")',590            'require("foo")'591          ].join('\n'),592          'foo.js': [593            '/**',594            ' * @providesModule foo',595            ' */',596            'require("aPackage")'597          ].join('\n'),598          'aPackage': {599            'package.json': JSON.stringify({600              name: 'aPackage',601              main: 'main.js'602            }),603            'main.js': 'main',604          }605        }606      });607      var dgraph = new DependencyGraph({608        roots: [root],609        fileWatcher: fileWatcher610      });611      return dgraph.load().then(function() {612        filesystem.root['bar.js'] = [613          '/**',614          ' * @providesModule bar',615          ' */',616          'require("foo")'617        ].join('\n');618        triggerFileChange('add', 'bar.js', root);619        filesystem.root.aPackage['main.js'] = 'require("bar")';620        triggerFileChange('change', 'aPackage/main.js', root);621        return dgraph.load().then(function() {622          expect(dgraph.getOrderedDependencies('/root/index.js'))623            .toEqual([624            { id: 'index',625              path: '/root/index.js',626              dependencies: ['aPackage', 'foo']627            },628            { id: 'aPackage/main',629              path: '/root/aPackage/main.js',630              dependencies: ['bar']631            },632            { id: 'bar',633              path: '/root/bar.js',634              dependencies: ['foo']635            },636            { id: 'foo',637              path: '/root/foo.js',638              dependencies: ['aPackage']639            },640          ]);641        });642      });643    });644    pit('runs changes through ignore filter', function() {645      var root = '/root';646      var filesystem = fs.__setMockFilesystem({647        'root': {648          'index.js': [649            '/**',650            ' * @providesModule index',651            ' */',652            'require("aPackage")',653            'require("foo")'654          ].join('\n'),655          'foo.js': [656            '/**',657            ' * @providesModule foo',658            ' */',659            'require("aPackage")'660          ].join('\n'),661          'aPackage': {662            'package.json': JSON.stringify({663              name: 'aPackage',664              main: 'main.js'665            }),666            'main.js': 'main',667          }668        }669      });670      var dgraph = new DependencyGraph({671        roots: [root],672        fileWatcher: fileWatcher,673        ignoreFilePath: function(filePath) {674          if (filePath === '/root/bar.js') {675            return true;676          }677          return false;678        }679      });680      return dgraph.load().then(function() {681        filesystem.root['bar.js'] = [682          '/**',683          ' * @providesModule bar',684          ' */',685          'require("foo")'686        ].join('\n');687        triggerFileChange('add', 'bar.js', root);688        filesystem.root.aPackage['main.js'] = 'require("bar")';689        triggerFileChange('change', 'aPackage/main.js', root);690        return dgraph.load().then(function() {691          expect(dgraph.getOrderedDependencies('/root/index.js'))692            .toEqual([693              { id: 'index',694                path: '/root/index.js',695                dependencies: ['aPackage', 'foo']696              },697              { id: 'aPackage/main',698                path: '/root/aPackage/main.js',699                dependencies: ['bar']700              },701              { id: 'foo',702                path: '/root/foo.js',703                dependencies: ['aPackage']704              },705            ]);706        });707      });708    });709    pit('should ignore directory updates', function() {710      var root = '/root';711      fs.__setMockFilesystem({712        'root': {713          'index.js': [714            '/**',715            ' * @providesModule index',716            ' */',717            'require("aPackage")',718            'require("foo")'719          ].join('\n'),720          'foo.js': [721            '/**',722            ' * @providesModule foo',723            ' */',724            'require("aPackage")'725          ].join('\n'),726          'aPackage': {727            'package.json': JSON.stringify({728              name: 'aPackage',729              main: 'main.js'730            }),731            'main.js': 'main',732          }733        }734      });735      var dgraph = new DependencyGraph({736        roots: [root],737        fileWatcher: fileWatcher738      });739      return dgraph.load().then(function() {740        triggerFileChange('change', 'aPackage', '/root', {741          isDirectory: function(){ return true; }742        });743        return dgraph.load().then(function() {744          expect(dgraph.getOrderedDependencies('/root/index.js'))745            .toEqual([746              { id: 'index',747                path: '/root/index.js',748                dependencies: ['aPackage', 'foo']749              },750              { id: 'aPackage/main',751                path: '/root/aPackage/main.js',752                dependencies: []753              },754              { id: 'foo',755                path: '/root/foo.js',756                dependencies: ['aPackage']757              },758            ]);759        });760      });761    });762  });...Node.js
Source:Node.js  
1module("tinymce.html.Node");2test('construction', function() {3	var node;4	expect(15);5	node = new tinymce.html.Node('#text', 3);6	equal(node.name, '#text');7	equal(node.type, 3);8	node = new tinymce.html.Node('#comment', 8);9	equal(node.name, '#comment');10	equal(node.type, 8);11	node = new tinymce.html.Node('b', 1);12	equal(node.name, 'b');13	equal(node.type, 1);14	deepEqual(node.attributes, []);15	node = new tinymce.html.Node('#pi', 7);16	equal(node.name, '#pi');17	equal(node.type, 7);18	node = new tinymce.html.Node('#doctype', 10);19	equal(node.name, '#doctype');20	equal(node.type, 10);21	node = new tinymce.html.Node('#cdata', 4);22	equal(node.name, '#cdata');23	equal(node.type, 4);24	node = new tinymce.html.Node('#frag', 11);25	equal(node.name, '#frag');26	equal(node.type, 11);27});28test('append inside empty node', function() {29	var root, node;30	expect(10);31	root = new tinymce.html.Node('#frag', 11);32	node = root.append(new tinymce.html.Node('b', 1));33	ok(root.firstChild.parent === root);34	equal(root.firstChild.next, undefined);35	equal(root.firstChild.prev, undefined);36	equal(root.firstChild.firstChild, undefined);37	equal(root.firstChild.lastChild, undefined);38	ok(node.parent === root);39	equal(node.next, undefined);40	equal(node.prev, undefined);41	equal(node.firstChild, undefined);42	equal(node.lastChild, undefined);43});44test('append node after node', function() {45	var root, node, node2;46	expect(17);47	root = new tinymce.html.Node('#frag', 11);48	node2 = root.append(new tinymce.html.Node('a', 1));49	node = root.append(new tinymce.html.Node('b', 1));50	ok(root.firstChild.parent === root, 'root.firstChild.parent === root');51	ok(root.firstChild === node2, 'root.firstChild');52	ok(root.lastChild === node, 'root.firstChild');53	ok(root.firstChild.next === node, 'root.firstChild.next');54	equal(root.firstChild.prev, undefined, 'root.firstChild.prev');55	equal(root.firstChild.firstChild, undefined, 'root.firstChild.firstChild');56	equal(root.firstChild.lastChild, undefined, 'root.firstChild.lastChild');57	ok(node2.parent === root, 'node2.parent === root');58	ok(node2.next === node, 'node2.next');59	equal(node2.prev, undefined, 'node2.prev');60	equal(node2.firstChild, undefined, 'node2.firstChild');61	equal(node2.lastChild, undefined, 'node2.lastChild');62	ok(node.parent === root, 'node.parent === root');63	equal(node.next, undefined, 'node.next');64	ok(node.prev === node2, 'node.prev');65	equal(node.firstChild, undefined, 'node.firstChild');66	equal(node.lastChild, undefined, 'node.lastChild');67});68test('append existing node before other existing node', function() {69	var root, node, node2;70	expect(8);71	root = new tinymce.html.Node('#frag', 11);72	node = root.append(new tinymce.html.Node('a', 1));73	node2 = root.append(new tinymce.html.Node('b', 1));74	root.append(node);75	ok(root.firstChild === node2, 'root.firstChild');76	ok(root.lastChild === node, 'root.lastChild');77	equal(node.next, undefined, 'node.next');78	ok(node.prev === node2, 'node.prev');79	ok(node.parent === root, 'node.parent');80	ok(node2.parent === root, 'node2.parent');81	equal(node2.prev, undefined, 'node2.prev');82	ok(node2.next === node, 'node2.next');83});84test('remove unattached node', function() {85	expect(1);86	ok(!new tinymce.html.Node('#text', 3).remove().parent);87});88test('remove single child', function() {89	var root, node;90	expect(6);91	root = new tinymce.html.Node('#frag', 11);92	node = root.append(new tinymce.html.Node('p', 1));93	node = root.firstChild.remove();94	equal(root.firstChild, undefined);95	equal(root.lastChild, undefined);96	equal(node.parent, undefined);97	equal(node.next, undefined);98	equal(node.prev, undefined);99	equal(node.name, 'p');100});101test('remove middle node', function() {102	var root, node, node2, node3;103	expect(9);104	root = new tinymce.html.Node('#frag', 11);105	node = root.append(new tinymce.html.Node('a', 1));106	node2 = root.append(new tinymce.html.Node('b', 1));107	node3 = root.append(new tinymce.html.Node('c', 1));108	node2.remove();109	equal(node2.parent, undefined);110	equal(node2.next, undefined);111	equal(node2.prev, undefined);112	ok(root.firstChild === node, 'root.firstChild');113	ok(root.lastChild === node3, 'root.lastChild');114	ok(node.next === node3, 'node.next');115	equal(node.prev, undefined, 'node.prev');116	ok(node3.prev, node, 'node3.prev');117	equal(node3.next, undefined, 'node3.next');118});119test('insert after last', function() {120	var fragment, root, node, node2;121	expect(5);122	fragment = new tinymce.html.Node('#frag', 11);123	root = fragment.append(new tinymce.html.Node('body', 1));124	node = root.append(new tinymce.html.Node('a', 1));125	node2 = root.insert(new tinymce.html.Node('x', 1), node);126	ok(root.firstChild === node, 'root.firstChild');127	ok(root.lastChild === node2, 'root.lastChild');128	ok(node.next === node2, 'node.next');129	ok(node2.prev === node, 'node2.prev');130	ok(node2.parent === root, 'node3.next');131});132test('insert before first', function() {133	var fragment, root, node, node2;134	expect(8);135	fragment = new tinymce.html.Node('#frag', 11);136	root = fragment.append(new tinymce.html.Node('body', 1));137	node = root.append(new tinymce.html.Node('a', 1));138	node2 = root.insert(new tinymce.html.Node('x', 1), node, true);139	ok(root.firstChild === node2, 'root.firstChild');140	ok(root.lastChild === node, 'root.lastChild');141	ok(node2.parent === root, 'node2.lastChild');142	ok(node2.next === node, 'node2.next');143	ok(node2.prev === undefined, 'node2.prev');144	ok(node.parent === root, 'node.lastChild');145	ok(node.next === undefined, 'node.next');146	ok(node.prev === node2, 'node.prev');147});148test('insert before second', function() {149	var fragment, root, node, node2, node3;150	expect(5);151	fragment = new tinymce.html.Node('#frag', 11);152	root = fragment.append(new tinymce.html.Node('body', 1));153	node = root.append(new tinymce.html.Node('a', 1));154	node2 = root.append(new tinymce.html.Node('b', 1));155	node3 = root.insert(new tinymce.html.Node('x', 1), node2, true);156	ok(root.firstChild === node, 'root.firstChild');157	ok(root.lastChild === node2, 'root.lastChild');158	ok(node3.parent === root, 'node3.parent');159	ok(node3.next === node2, 'node3.next');160	ok(node3.prev === node, 'node3.prev');161});162test('insert after and between two nodes', function() {163	var root, node, node2, node3, fragment;164	expect(7);165	fragment = new tinymce.html.Node('#frag', 11);166	root = fragment.append(new tinymce.html.Node('body', 1));167	node = root.append(new tinymce.html.Node('a', 1));168	node2 = root.append(new tinymce.html.Node('b', 1));169	node3 = root.insert(new tinymce.html.Node('x', 1), node);170	ok(root.firstChild === node, 'root.firstChild');171	ok(root.lastChild === node2, 'root.lastChild');172	ok(node.next === node3, 'node.next');173	ok(node2.prev === node3, 'node2.prev');174	ok(node3.parent === root, 'node3.next');175	ok(node3.next === node2, 'node3.next');176	ok(node3.prev === node, 'node3.prev');177});178test('replace single child', function() {179	var root, node1, node2;180	expect(5);181	root = new tinymce.html.Node('#frag', 11);182	node1 = root.append(new tinymce.html.Node('b', 1));183	node2 = root.append(new tinymce.html.Node('em', 1));184	node1.replace(node2);185	ok(root.firstChild === node2, 'root.firstChild');186	ok(root.lastChild === node2, 'root.lastChild');187	ok(node2.parent === root, 'node2.parent');188	ok(!node2.next, 'node2.next');189	ok(!node2.prev, 'node2.prev');190});191test('replace first child', function() {192	var root, node1, node2, node3;193	expect(5);194	root = new tinymce.html.Node('#frag', 11);195	node1 = root.append(new tinymce.html.Node('b', 1));196	node2 = root.append(new tinymce.html.Node('em', 1));197	node3 = root.append(new tinymce.html.Node('b', 1));198	node1.replace(node2);199	ok(root.firstChild === node2, 'root.firstChild');200	ok(root.lastChild === node3, 'root.lastChild');201	ok(node2.parent === root, 'node2.parent');202	ok(node2.next === node3, 'node2.next');203	ok(!node2.prev, 'node2.prev');204});205test('replace last child', function() {206	var root, node1, node2, node3;207	expect(5);208	root = new tinymce.html.Node('#frag', 11);209	node1 = root.append(new tinymce.html.Node('b', 1));210	node3 = root.append(new tinymce.html.Node('b', 1));211	node2 = root.append(new tinymce.html.Node('em', 1));212	node3.replace(node2);213	ok(root.firstChild === node1, 'root.firstChild');214	ok(root.lastChild === node2, 'root.lastChild');215	ok(node2.parent === root, 'node2.parent');216	ok(!node2.next, 'node2.next');217	ok(node2.prev === node1, 'node2.prev');218});219test('replace middle child', function() {220	var root, node1, node2, node3, node4;221	expect(5);222	root = new tinymce.html.Node('#frag', 11);223	node1 = root.append(new tinymce.html.Node('b', 1));224	node2 = root.append(new tinymce.html.Node('b', 1));225	node3 = root.append(new tinymce.html.Node('b', 1));226	node4 = root.append(new tinymce.html.Node('em', 1));227	node2.replace(node4);228	ok(root.firstChild === node1, 'root.firstChild');229	ok(root.lastChild === node3, 'root.lastChild');230	ok(node4.parent === root, 'node4.parent');231	ok(node4.next === node3, 'node4.next');232	ok(node4.prev === node1, 'node4.prev');233});234test('attr', 22, function() {235	var node;236	node = new tinymce.html.Node('b', 1);237	deepEqual(node.attributes, []);238	node.attr('attr1', 'value1');239	equal(node.attr('attr1'), 'value1');240	equal(node.attr('attr2'), undefined);241	deepEqual(node.attributes, [{name: 'attr1', value: 'value1'}]);242	deepEqual(node.attributes.map, {'attr1': 'value1'});243	node = new tinymce.html.Node('b', 1);244	deepEqual(node.attributes, []);245	node.attr('attr1', 'value1');246	node.attr('attr1', 'valueX');247	equal(node.attr('attr1'), 'valueX');248	deepEqual(node.attributes, [{name: 'attr1', value: 'valueX'}]);249	deepEqual(node.attributes.map, {'attr1': 'valueX'});250	node = new tinymce.html.Node('b', 1);251	deepEqual(node.attributes, []);252	node.attr('attr1', 'value1');253	node.attr('attr2', 'value2');254	equal(node.attr('attr1'), 'value1');255	equal(node.attr('attr2'), 'value2');256	deepEqual(node.attributes, [{name: 'attr1', value: 'value1'}, {name: 'attr2', value: 'value2'}]);257	deepEqual(node.attributes.map, {'attr1': 'value1', 'attr2': 'value2'});258	node = new tinymce.html.Node('b', 1);259	deepEqual(node.attributes, []);260	node.attr('attr1', 'value1');261	node.attr('attr1', null);262	equal(node.attr('attr1'), undefined);263	deepEqual(node.attributes, []);264	deepEqual(node.attributes.map, {});265	node = new tinymce.html.Node('b', 1);266	node.attr({a:'1', b:'2'});267	deepEqual(node.attributes, [{name: 'a', value: '1'}, {name: 'b', value: '2'}]);268	deepEqual(node.attributes.map, {a:'1', b:'2'});269	node = new tinymce.html.Node('b', 1);270	node.attr(null);271	deepEqual(node.attributes, []);272	deepEqual(node.attributes.map, {});273});274test('clone', function() {275	var root, node, clone;276	expect(16);277	node = new tinymce.html.Node('#text', 3);278	node.value = 'value';279	clone = node.clone();280	equal(clone.name, '#text');281	equal(clone.type, 3);282	equal(clone.value, 'value');283	equal(clone.parent, undefined);284	equal(clone.next, undefined);285	equal(clone.prev, undefined);286	root = new tinymce.html.Node('#frag', 11);287	node = new tinymce.html.Node('#text', 3);288	node.value = 'value';289	root.append(node);290	equal(clone.name, '#text');291	equal(clone.type, 3);292	equal(clone.value, 'value');293	equal(clone.parent, undefined);294	equal(clone.next, undefined);295	equal(clone.prev, undefined);296	node = new tinymce.html.Node('b', 1);297	node.attr('id', 'id');298	node.attr('class', 'class');299	node.attr('title', 'title');300	clone = node.clone();301	equal(clone.name, 'b');302	equal(clone.type, 1);303	deepEqual(clone.attributes, [{name: 'class', value: 'class'}, {name: 'title', value: 'title'}]);304	deepEqual(clone.attributes.map, {'class': 'class', 'title': 'title'});305});306test('unwrap', function() {307	var root, node1, node2, node3;308	expect(7);309	root = new tinymce.html.Node('#frag', 11);310	node1 = root.append(new tinymce.html.Node('b', 1));311	node2 = node1.append(new tinymce.html.Node('em', 1));312	node1.unwrap();313	ok(root.firstChild === node2, 'root.firstChild');314	ok(root.lastChild === node2, 'root.lastChild');315	ok(node2.parent === root, 'node2.parent');316	root = new tinymce.html.Node('#frag', 11);317	node1 = root.append(new tinymce.html.Node('b', 1));318	node2 = node1.append(new tinymce.html.Node('em', 1));319	node3 = node1.append(new tinymce.html.Node('span', 1));320	node1.unwrap();321	ok(root.firstChild === node2, 'root.firstChild');322	ok(root.lastChild === node3, 'root.lastChild');323	ok(node2.parent === root, 'node2.parent');324	ok(node3.parent === root, 'node3.parent');325});326test('empty', function() {327	var root, node1, node2;328	expect(4);329	root = new tinymce.html.Node('#frag', 11);330	node1 = root.append(new tinymce.html.Node('b', 1));331	node2 = node1.append(new tinymce.html.Node('em', 1));332	node1.empty();333	ok(root.firstChild === node1, 'root.firstChild');334	ok(root.lastChild === node1, 'root.firstChild');335	ok(!node1.firstChild, 'node1.firstChild');336	ok(!node1.lastChild, 'node1.firstChild');337});338test('isEmpty', function() {339	var root, node1, node2;340	expect(9);341	root = new tinymce.html.Node('#frag', 11);342	node1 = root.append(new tinymce.html.Node('p', 1));343	node2 = node1.append(new tinymce.html.Node('b', 1));344	ok(root.isEmpty({img: 1}), 'Is empty 1');345	ok(node1.isEmpty({img: 1}), 'Is empty 2');346	root = new tinymce.html.Node('#frag', 11);347	node1 = root.append(new tinymce.html.Node('p', 1));348	node2 = node1.append(new tinymce.html.Node('img', 1));349	ok(!root.isEmpty({img: 1}), 'Is not empty 1');350	ok(!node1.isEmpty({img: 1}), 'Is not empty 2');351	root = new tinymce.html.Node('#frag', 11);352	node1 = root.append(new tinymce.html.Node('p', 1));353	node2 = node1.append(new tinymce.html.Node('#text', 3));354	node2.value = 'X';355	ok(!root.isEmpty({img: 1}), 'Is not empty 3');356	ok(!node1.isEmpty({img: 1}), 'Is not empty 4');357	root = new tinymce.html.Node('#frag', 11);358	node1 = root.append(new tinymce.html.Node('p', 1));359	node2 = node1.append(new tinymce.html.Node('#text', 3));360	node2.value = '';361	ok(root.isEmpty({img: 1}), 'Is empty 4');362	ok(node1.isEmpty({img: 1}), 'Is empty 5');363	root = new tinymce.html.Node('#frag', 11);364	node1 = root.append(new tinymce.html.Node('a', 1)).attr('name', 'x');365	ok(!root.isEmpty({img: 1}), 'Contains anchor with name attribute.');...CaretBookmark.js
Source:CaretBookmark.js  
1ModuleLoader.require([2	'tinymce/caret/CaretBookmark',3	'tinymce/caret/CaretPosition'4], function(CaretBookmark, CaretPosition) {5	var assertCaretPosition = Utils.assertCaretPosition;6	module('tinymce.caret.CaretBookmark');7	function getRoot() {8		return document.getElementById('view');9	}10	function setupHtml(html) {11		getRoot().innerHTML = html;12	}13	function createTextPos(textNode, offset) {14		return new CaretPosition(textNode, offset);15	}16	test('create element index', function() {17		setupHtml('<b></b><i></i><b></b>');18		equal(CaretBookmark.create(getRoot(), CaretPosition.before(getRoot().childNodes[0])), 'b[0],before');19		equal(CaretBookmark.create(getRoot(), CaretPosition.before(getRoot().childNodes[1])), 'i[0],before');20		equal(CaretBookmark.create(getRoot(), CaretPosition.before(getRoot().childNodes[2])), 'b[1],before');21		equal(CaretBookmark.create(getRoot(), CaretPosition.after(getRoot().childNodes[2])), 'b[1],after');22	});23	test('create text index', function() {24		setupHtml('a<b></b>b<b></b>ccc');25		equal(CaretBookmark.create(getRoot(), createTextPos(getRoot().childNodes[0], 0)), 'text()[0],0');26		equal(CaretBookmark.create(getRoot(), createTextPos(getRoot().childNodes[2], 1)), 'text()[1],1');27		equal(CaretBookmark.create(getRoot(), createTextPos(getRoot().childNodes[4], 3)), 'text()[2],3');28	});29	test('create text index on fragmented text nodes', function() {30		setupHtml('a');31		getRoot().appendChild(document.createTextNode('b'));32		getRoot().appendChild(document.createTextNode('c'));33		getRoot().appendChild(document.createElement('b'));34		getRoot().appendChild(document.createTextNode('d'));35		getRoot().appendChild(document.createTextNode('e'));36		equal(getRoot().childNodes.length, 6);37		equal(CaretBookmark.create(getRoot(), createTextPos(getRoot().childNodes[0], 0)), 'text()[0],0');38		equal(CaretBookmark.create(getRoot(), createTextPos(getRoot().childNodes[1], 0)), 'text()[0],1');39		equal(CaretBookmark.create(getRoot(), createTextPos(getRoot().childNodes[2], 0)), 'text()[0],2');40		equal(CaretBookmark.create(getRoot(), createTextPos(getRoot().childNodes[4], 0)), 'text()[1],0');41		equal(CaretBookmark.create(getRoot(), createTextPos(getRoot().childNodes[5], 0)), 'text()[1],1');42	});43	test('create br element index', function() {44		setupHtml('<p><br data-mce-bogus="1"></p><p><br></p>');45		equal(CaretBookmark.create(getRoot(), CaretPosition.before(getRoot().firstChild.firstChild)), 'p[0]/br[0],before');46		equal(CaretBookmark.create(getRoot(), CaretPosition.before(getRoot().lastChild.firstChild)), 'p[1]/br[0],before');47	});48	test('create deep element index', function() {49		setupHtml('<p><span>a</span><span><b id="a"></b><b id="b"></b><b id="c"></b></span></p>');50		equal(CaretBookmark.create(getRoot(), CaretPosition.before(document.getElementById('a'))), 'p[0]/span[1]/b[0],before');51		equal(CaretBookmark.create(getRoot(), CaretPosition.before(document.getElementById('b'))), 'p[0]/span[1]/b[1],before');52		equal(CaretBookmark.create(getRoot(), CaretPosition.before(document.getElementById('c'))), 'p[0]/span[1]/b[2],before');53		equal(CaretBookmark.create(getRoot(), CaretPosition.after(document.getElementById('c'))), 'p[0]/span[1]/b[2],after');54	});55	test('create deep text index', function() {56		setupHtml('<p><span>a</span><span id="x">a<b></b>b<b></b>ccc</span></p>');57		equal(CaretBookmark.create(getRoot(), createTextPos(document.getElementById('x').childNodes[0], 0)), 'p[0]/span[1]/text()[0],0');58		equal(CaretBookmark.create(getRoot(), createTextPos(document.getElementById('x').childNodes[2], 1)), 'p[0]/span[1]/text()[1],1');59		equal(CaretBookmark.create(getRoot(), createTextPos(document.getElementById('x').childNodes[4], 3)), 'p[0]/span[1]/text()[2],3');60	});61	test('create element index from bogus', function() {62		setupHtml('<b></b><span data-mce-bogus="1"><b></b><span data-mce-bogus="1"><b></b><b></b></span></span>');63		equal(CaretBookmark.create(getRoot(), CaretPosition.before(getRoot().lastChild.lastChild.childNodes[1])), 'b[3],before');64	});65	test('resolve element index', function() {66		setupHtml('<b></b><i></i><b></b>');67		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'b[0],before'), CaretPosition.before(getRoot().childNodes[0]));68		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'b[1],before'), CaretPosition.before(getRoot().childNodes[2]));69		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'b[1],after'), CaretPosition.after(getRoot().childNodes[2]));70		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'i[0],before'), CaretPosition.before(getRoot().childNodes[1]));71	});72	test('resolve odd element names', function() {73		setupHtml('<h-2X>abc</h-2X>');74		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'h-2X[0]/text()[0],2'), createTextPos(getRoot().childNodes[0].firstChild, 2));75	});76	test('resolve deep element index', function() {77		setupHtml('<p><span>a</span><span><b id="a"></b><b id="b"></b><b id="c"></b></span></p>');78		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'p[0]/span[1]/b[0],before'), CaretPosition.before(document.getElementById('a')));79		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'p[0]/span[1]/b[1],before'), CaretPosition.before(document.getElementById('b')));80		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'p[0]/span[1]/b[2],before'), CaretPosition.before(document.getElementById('c')));81		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'p[0]/span[1]/b[2],after'), CaretPosition.after(document.getElementById('c')));82	});83	test('resolve text index', function() {84		setupHtml('a<b></b>b<b></b>ccc');85		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'text()[0],0'), createTextPos(getRoot().childNodes[0], 0));86		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'text()[1],1'), createTextPos(getRoot().childNodes[2], 1));87		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'text()[2],3'), createTextPos(getRoot().childNodes[4], 3));88	});89	test('resolve text index on fragmented text nodes', function() {90		setupHtml('a');91		getRoot().appendChild(document.createTextNode('b'));92		getRoot().appendChild(document.createTextNode('c'));93		getRoot().appendChild(document.createElement('b'));94		getRoot().appendChild(document.createTextNode('d'));95		getRoot().appendChild(document.createTextNode('e'));96		equal(getRoot().childNodes.length, 6);97		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'text()[0],0'), createTextPos(getRoot().childNodes[0], 0));98		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'text()[0],1'), createTextPos(getRoot().childNodes[0], 1));99		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'text()[0],2'), createTextPos(getRoot().childNodes[1], 1));100		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'text()[0],3'), createTextPos(getRoot().childNodes[2], 1));101		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'text()[0],4'), createTextPos(getRoot().childNodes[2], 1));102		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'text()[1],0'), createTextPos(getRoot().childNodes[4], 0));103		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'text()[1],1'), createTextPos(getRoot().childNodes[4], 1));104		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'text()[1],2'), createTextPos(getRoot().childNodes[5], 1));105	});106	test('resolve text index with to high offset', function() {107		setupHtml('abc');108		assertCaretPosition(CaretBookmark.resolve(getRoot(), 'text()[0],10'), createTextPos(getRoot().childNodes[0], 3));109	});110	test('resolve invalid paths', function() {111		setupHtml('<b><i></i></b>');112		equal(CaretBookmark.resolve(getRoot(), 'x[0]/y[1]/z[2]'), null);113		equal(CaretBookmark.resolve(getRoot(), 'b[0]/i[2]'), null);114		equal(CaretBookmark.resolve(getRoot(), 'x'), null);115		equal(CaretBookmark.resolve(getRoot(), null), null);116	});...Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!
