@testing-library/dom#waitFor TypeScript Examples

The following examples show how to use @testing-library/dom#waitFor. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: ChatMessages.test.tsx    From glific-frontend with GNU Affero General Public License v3.0 6 votes vote down vote up
test('Should render for multi-search', async () => {
  defineUrl('http://localhost:3000/chat/2?search=8');

  const { getByTestId } = render(chatMessages);

  await waitFor(() => {
    const container: any = document.querySelector('.messageContainer');
    fireEvent.scroll(container, { target: { scrollY: 0 } });
    fireEvent.click(getByTestId('loadMoreMessages'));
  });
});
Example #2
Source File: Setup.test.tsx    From legend-studio with Apache License 2.0 6 votes vote down vote up
test(
  integrationTest('Disable project selector when there is no projects'),
  async () => {
    MOBX__enableSpyOrMock();
    jest.spyOn(sdlcServerClient, 'getProjects').mockResolvedValue([]);
    MOBX__disableSpyOrMock();
    TEST__provideMockedWebApplicationNavigator();

    const { queryByText } = render(
      <MemoryRouter>
        <TEST__ApplicationStoreProvider
          config={TEST__getTestStudioConfig()}
          pluginManager={LegendStudioPluginManager.create()}
        >
          <TEST__SDLCServerClientProvider>
            <Setup />
          </TEST__SDLCServerClientProvider>
        </TEST__ApplicationStoreProvider>
      </MemoryRouter>,
    );

    await waitFor(() =>
      expect(
        queryByText(
          'You have no projects, please create or acquire access for at least one',
        ),
      ).not.toBeNull(),
    );
  },
);
Example #3
Source File: ChatMessages.test.tsx    From glific-frontend with GNU Affero General Public License v3.0 6 votes vote down vote up
// test('cancel after dialog box open', async () => {
//   const { getByText, getByTestId } = render(chatMessages);
//   await waitFor(() => {
//     fireEvent.click(getByTestId('messageOptions'));
//     fireEvent.click(getByTestId('dialogButton'));
//   });

//   fireEvent.click(getByText('Cancel'));
// });

// Need to first scroll up

test('click on Jump to latest', async () => {
  const { getByTestId } = render(chatMessages);
  const messageContainer: any = document.querySelector('.messageContainer');

  await act(async () => {
    await new Promise((r) => setTimeout(r, 1500));
  });

  fireEvent.scroll(messageContainer, { target: { scrollTop: 10 } });

  await waitFor(() => {
    fireEvent.click(getByTestId('jumpToLatest'));
  });
});
Example #4
Source File: Select.test.tsx    From chroma-react with MIT License 6 votes vote down vote up
test('it *does not* render errorMessage when not hasError', async () => {
  const props = getBaseProps();
  const { queryByText } = renderWithTheme(
    <Select {...props} data-testid={testId} errorMessage="should not show up" />
  );

  await act(async () => {
    await waitFor(() => {
      expect(queryByText('should not show up')).not.toBeInTheDocument();
    });
  });
});
Example #5
Source File: cancel-token.ts    From vue-concurrency with MIT License 6 votes vote down vote up
describe("getCancelToken", () => {
  it("works", async () => {
    const { promise, reject } = defer();
    const signalMock = {
      pr: promise,
    } as AbortSignalWithPromise;

    const cancelFn = jest.fn();
    class CancelToken {
      constructor(cb: (cancel: () => void) => void) {
        cb(cancelFn);
      }
    }

    const axiosMock = {
      CancelToken,
    };

    getCancelToken(axiosMock, signalMock);

    expect(cancelFn).not.toBeCalled();

    reject("cancel");

    await waitFor(() => expect(cancelFn).toHaveBeenCalled());
  });
});
Example #6
Source File: NewFileWizard.test.tsx    From pybricks-code with MIT License 6 votes vote down vote up
describe('cancel', () => {
    it('should dispatch cancel when close button is clicked', async () => {
        const [user, dialog, dispatch] = testRender(<NewFileWizard />, {
            explorer: { newFileWizard: { isOpen: true } },
        });

        await user.click(dialog.getByRole('button', { name: 'Close' }));

        expect(dispatch).toHaveBeenCalledWith(newFileWizardDidCancel());
    });

    it('should dispatch cancel when escape button is pressed', async () => {
        const [user, dialog, dispatch] = testRender(<NewFileWizard />, {
            explorer: { newFileWizard: { isOpen: true } },
        });

        await waitFor(() =>
            expect(dialog.getByRole('textbox', { name: 'File name' })).toHaveFocus(),
        );

        // FIXME: use userEvent instead of fireEvent
        // blocked by https://github.com/palantir/blueprint/pull/5349
        // await user.keyboard('{Escape}');
        user;
        fireEvent.keyDown(document.activeElement ?? document, {
            key: 'Escape',
            keyCode: 27,
            which: 27,
        });

        expect(dispatch).toHaveBeenCalledWith(newFileWizardDidCancel());
    });
});
Example #7
Source File: task-multiple.ts    From vue-concurrency with MIT License 6 votes vote down vote up
describe("useTask | multiple | task", () => {
  test("can yield instance of another task", async () => {
    await mockSetup(async () => {
      const childTask = useTask(function* () {
        yield wait(20);
        return "foo";
      });
      const mainTask = useTask(function* () {
        const a: YieldReturn<typeof childTask> = yield childTask.perform();
        const b: YieldReturn<typeof childTask> = yield childTask.perform();
        return a + b;
      });

      mainTask.perform();

      await waitFor(() => expect(mainTask.last?.value).toBe("foofoo"));
    });
  });

  test("task picks up thrown error of another task", async () => {
    await mockSetup(async () => {
      const error = new Error("Task error");
      const childTask = useTask(function* () {
        throw error;
      });
      const mainTask = useTask(function* () {
        return yield childTask.perform();
      });

      mainTask.perform();

      await waitFor(() => expect(mainTask.last?.error).toBe(error));
    });
  });
});
Example #8
Source File: NewFileWizard.test.tsx    From pybricks-code with MIT License 6 votes vote down vote up
describe('accept', () => {
    it('should dispatch accept action when button is clicked', async () => {
        const [user, dialog, dispatch] = testRender(<NewFileWizard />, {
            explorer: { newFileWizard: { isOpen: true } },
        });

        const button = dialog.getByLabelText('Create');

        // have to type a file name before Create button is enabled
        await user.type(dialog.getByRole('textbox', { name: 'File name' }), 'test');
        await waitFor(() => expect(button).not.toBeDisabled());

        await user.click(button);
        expect(dispatch).toHaveBeenCalledWith(
            newFileWizardDidAccept('test', '.py', Hub.Technic),
        );
    });

    it('should dispatch accept action when enter is pressed ', async () => {
        const [user, dialog, dispatch] = testRender(<NewFileWizard />, {
            explorer: { newFileWizard: { isOpen: true } },
        });

        await user.type(
            dialog.getByRole('textbox', { name: 'File name' }),
            'test{Enter}',
        );

        expect(dispatch).toHaveBeenCalledWith(
            newFileWizardDidAccept('test', '.py', Hub.Technic),
        );
    });
});
Example #9
Source File: index.test.tsx    From oasis-wallet-web with Apache License 2.0 6 votes vote down vote up
describe('<NetworkSelector  />', () => {
  let store: ReturnType<typeof configureAppStore>

  beforeEach(() => {
    store = configureAppStore()
  })

  it('should match snapshot', () => {
    const component = renderComponent(store)
    expect(component.container.firstChild).toMatchSnapshot()
  })

  it('should allow switching network', async () => {
    const dispatchSpy = jest.spyOn(store, 'dispatch')
    const component = renderComponent(store)
    expect(component.queryByTestId('active-network')).toContainHTML('toolbar.networks.local')
    userEvent.click(screen.getByTestId('network-selector'))

    await waitFor(() => expect(screen.getByText('toolbar.networks.testnet')))
    screen.getByText('toolbar.networks.testnet').click()

    expect(dispatchSpy).toHaveBeenCalledWith({
      payload: 'testnet',
      type: 'network/selectNetwork',
    })
  })
})
Example #10
Source File: Snackbar.test.tsx    From chroma-react with MIT License 6 votes vote down vote up
test('it renders a dismissible Snackbar', async () => {
  const mockFn = jest.fn();

  const { findByLabelText } = renderWithTheme(
    <Snackbar {...getBaseProps()} allowDismiss onClose={mockFn} />
  );

  const button = await findByLabelText('Close Notification');
  fireEvent.click(button);

  await waitFor(() => expect(mockFn).toBeCalledTimes(1));
});
Example #11
Source File: ChatMessages.test.tsx    From glific-frontend with GNU Affero General Public License v3.0 5 votes vote down vote up
it('should have title as group name', async () => {
  const { getByTestId } = render(chatMessagesWithCollection);
  await waitFor(() => {
    expect(getByTestId('beneficiaryName')).toHaveTextContent('Default Group');
  });
});
Example #12
Source File: sagas.test.ts    From pybricks-code with MIT License 5 votes vote down vote up
describe('handleShowAlert', () => {
    let toaster: TestToaster;
    let saga: AsyncSaga;

    beforeEach(async () => {
        toaster = new TestToaster();
        jest.spyOn(toaster, 'show');
        jest.spyOn(toaster, 'dismiss');
        saga = new AsyncSaga(alerts, { toaster });
    });

    it('should show toast', async () => {
        saga.put(
            alertsShowAlert('alerts', 'unexpectedError', {
                error: { name: 'TestError', message: 'test error' },
            }),
        );

        expect(toaster.dismiss).not.toHaveBeenCalled();
        expect(toaster.show).toHaveBeenCalled();

        toaster.dismiss(toaster.getToasts().at(-1)?.key ?? '');

        await expect(saga.take()).resolves.toEqual(
            alertsDidShowAlert('alerts', 'unexpectedError', 'dismiss'),
        );
    });

    it('should show close and re-open toast with same key', async () => {
        // request to show the same alert twice
        saga.put(
            alertsShowAlert('alerts', 'unexpectedError', {
                error: { name: 'TestError', message: 'test error' },
            }),
        );
        saga.put(
            alertsShowAlert('alerts', 'unexpectedError', {
                error: { name: 'TestError', message: 'test error' },
            }),
        );

        // at this point, show has only been called once to display the first alert
        expect(toaster.show).toHaveBeenCalled();
        // and then dismiss was called to close it
        expect(toaster.dismiss).toHaveBeenCalled();

        // which should result in an action
        await expect(saga.take()).resolves.toEqual(
            alertsDidShowAlert('alerts', 'unexpectedError', 'dismiss'),
        );

        // then after a delay, the second alert is shown
        await waitFor(() => expect(toaster.show).toHaveBeenCalledTimes(2));

        // then we dismiss it manually, like normal
        toaster.dismiss(toaster.getToasts().at(-1)?.key ?? '');

        // and get the action for the second dismiss
        await expect(saga.take()).resolves.toEqual(
            alertsDidShowAlert('alerts', 'unexpectedError', 'dismiss'),
        );
    });

    afterEach(async () => {
        await saga.end();
    });
});
Example #13
Source File: ChatMessages.test.tsx    From glific-frontend with GNU Affero General Public License v3.0 5 votes vote down vote up
test('Load more messages', async () => {
  const searchQuery = {
    query: SEARCH_QUERY,
    variables: {
      filter: {},
      contactOpts: { limit: DEFAULT_CONTACT_LIMIT },
      messageOpts: { limit: DEFAULT_MESSAGE_LIMIT },
    },
    data: {
      search: [
        {
          group: null,
          contact: {
            id: '2',
            name: 'Effie Cormier',
            phone: '987654321',
            maskedPhone: '98****321',
            lastMessageAt: '2020-06-29T09:31:47Z',
            status: 'VALID',
            fields: '{}',
            bspStatus: 'SESSION_AND_HSM',
            isOrgRead: true,
          },
          messages,
        },
      ],
    },
  };

  cache.writeQuery(searchQuery);
  const client = new ApolloClient({
    cache: cache,
    assumeImmutableResults: true,
  });

  const chatMessages = (
    <MemoryRouter>
      <ApolloProvider client={client}>
        <ChatMessages contactId="2" />
      </ApolloProvider>
    </MemoryRouter>
  );

  const { getByTestId } = render(chatMessages);

  await waitFor(() => {
    const container: any = document.querySelector('.messageContainer');
    fireEvent.scroll(container, { target: { scrollY: 0 } });
    fireEvent.click(getByTestId('loadMoreMessages'));
  });
});
Example #14
Source File: RenameFileDialog.test.tsx    From pybricks-code with MIT License 5 votes vote down vote up
describe('rename button', () => {
    it('should accept the dialog Rename is clicked', async () => {
        const [user, dialog, dispatch] = testRender(<RenameFileDialog />, {
            explorer: { renameFileDialog: { isOpen: true, fileName: 'old.file' } },
        });

        const button = dialog.getByRole('button', { name: 'Rename' });

        // have to type a new file name before Rename button is enabled
        const input = dialog.getByLabelText('File name');
        await waitFor(() => expect(input).toHaveFocus());
        await user.type(input, 'new', { skipClick: true });
        await waitFor(() => expect(button).not.toBeDisabled());

        await user.click(button);
        expect(dispatch).toHaveBeenCalledWith(
            renameFileDialogDidAccept('old.file', 'new.file'),
        );
    });

    it('should accept the dialog when enter is pressed in the text input', async () => {
        const [user, dialog, dispatch] = testRender(<RenameFileDialog />, {
            explorer: { renameFileDialog: { isOpen: true, fileName: 'old.file' } },
        });

        // have to type a new file name before Rename button is enabled
        const input = dialog.getByLabelText('File name');
        await waitFor(() => expect(input).toHaveFocus());
        await user.type(input, 'new{Enter}', { skipClick: true });

        expect(dispatch).toHaveBeenCalledWith(
            renameFileDialogDidAccept('old.file', 'new.file'),
        );
    });

    it('should be cancellable', async () => {
        const [user, dialog, dispatch] = testRender(<RenameFileDialog />, {
            explorer: { renameFileDialog: { isOpen: true } },
        });

        const button = dialog.getByRole('button', { name: 'Close' });

        await waitFor(() => expect(button).toBeVisible());

        await user.click(button);
        expect(dispatch).toHaveBeenCalledWith(renameFileDialogDidCancel());
    });
});
Example #15
Source File: QueryBuilder_Milestoning.test.tsx    From legend-studio with Apache License 2.0 5 votes vote down vote up
test(
  integrationTest(
    'Query builder state is properly set after processing a lambda with non-temporal source Business Temporal target',
  ),
  async () => {
    const pluginManager = LegendQueryPluginManager.create();
    pluginManager.usePresets([new Query_GraphPreset()]).install();
    const mockedQueryStore = TEST__provideMockedLegendQueryStore({
      pluginManager,
    });
    const renderResult = await TEST__setUpQueryEditor(
      mockedQueryStore,
      TEST_MilestoningModel,
      stub_RawLambda(),
      'my::map',
      'my::runtime',
    );
    const queryBuilderState = mockedQueryStore.queryBuilderState;

    const _personClass =
      mockedQueryStore.graphManagerState.graph.getClass('my::Firm');
    act(() => {
      queryBuilderState.changeClass(_personClass);
    });
    const queryBuilderSetup = await waitFor(() =>
      renderResult.getByTestId(QUERY_BUILDER_TEST_ID.QUERY_BUILDER_SETUP),
    );
    await waitFor(() => getByText(queryBuilderSetup, 'Firm'));
    await waitFor(() => getByText(queryBuilderSetup, 'map'));
    await waitFor(() => getByText(queryBuilderSetup, 'runtime'));

    act(() => {
      queryBuilderState.initialize(
        create_RawLambda(
          TEST_DATA__simpleProjectionWithNonTemporalSourceAndBusinessTemporalTarget.parameters,
          TEST_DATA__simpleProjectionWithNonTemporalSourceAndBusinessTemporalTarget.body,
        ),
      );
    });
    const projectionColumnState = guaranteeType(
      queryBuilderState.fetchStructureState.projectionState.columns[0],
      QueryBuilderSimpleProjectionColumnState,
    );
    const derivedPropertyExpressionStates =
      projectionColumnState.propertyExpressionState
        .derivedPropertyExpressionStates;

    // property replaced with derived property as it is milestoned
    expect(derivedPropertyExpressionStates.length).toBe(1);
    const parameterValues = guaranteeNonNullable(
      derivedPropertyExpressionStates[0]?.propertyExpression.parametersValues,
    );

    // default milestoning date is propagated as date propagation is not supported.
    expect(parameterValues.length).toBe(2);
  },
);
Example #16
Source File: task-concurrency-restartable.ts    From vue-concurrency with MIT License 5 votes vote down vote up
describe("useTask | restartable task", () => {
  test("runs the first task instance right away", async () => {
    await mockSetup(() => {
      const task = useTask(function* () { }).restartable();
      const taskInstance = task.perform();
      expect(taskInstance.isRunning).toBe(true);
    });
  });

  test("cancels first running task when the task is performed again", async () => {
    await mockSetup(async () => {
      const task = useTask(function* () {
        yield wait(10);
      }).restartable();
      const taskInstance1 = task.perform();
      await wait(5);
      const taskInstance2 = task.perform();
      expect(taskInstance1.isCanceling).toBe(true);
      expect(taskInstance2.isRunning).toBe(true);
      const taskInstance3 = task.perform();
      expect(taskInstance2.isCanceling).toBe(true);
      expect(taskInstance3.isRunning).toBe(true);
    });
  });

  test("cancels first running task when maxConcurrency is reached", async () => {
    await mockSetup(async () => {
      const task = useTask(function* () {
        yield wait(50);
      })
        .restartable()
        .maxConcurrency(3);
      const [instance1, instance2, instance3] = perform3x(task);
      expect(instance1.isRunning).toBe(true);
      expect(instance2.isRunning).toBe(true);
      expect(instance3.isRunning).toBe(true);

      const instance4 = task.perform();
      expect(instance1.isCanceling).toBe(true);
      expect(instance2.isRunning).toBe(true);
      expect(instance3.isRunning).toBe(true);
      expect(instance4.isRunning).toBe(true);

      const instance5 = task.perform();
      await waitFor(() => expect(instance2.isCanceling).toBe(true));
      expect(instance3.isRunning).toBe(true);
      expect(instance4.isRunning).toBe(true);
      expect(instance5.isRunning).toBe(true);
    });
  });
});
Example #17
Source File: QueryBuilder_Milestoning.test.tsx    From legend-studio with Apache License 2.0 5 votes vote down vote up
test(
  integrationTest(
    'Query builder state is properly set after processing a lambda with non-temporal source BiTemporal target',
  ),
  async () => {
    const pluginManager = LegendQueryPluginManager.create();
    pluginManager.usePresets([new Query_GraphPreset()]).install();
    const mockedQueryStore = TEST__provideMockedLegendQueryStore({
      pluginManager,
    });
    const renderResult = await TEST__setUpQueryEditor(
      mockedQueryStore,
      TEST_MilestoningModel,
      stub_RawLambda(),
      'my::map',
      'my::runtime',
    );
    const queryBuilderState = mockedQueryStore.queryBuilderState;

    const _personClass =
      mockedQueryStore.graphManagerState.graph.getClass('my::Firm');
    act(() => {
      queryBuilderState.changeClass(_personClass);
    });
    const queryBuilderSetup = await waitFor(() =>
      renderResult.getByTestId(QUERY_BUILDER_TEST_ID.QUERY_BUILDER_SETUP),
    );
    await waitFor(() => getByText(queryBuilderSetup, 'Firm'));
    await waitFor(() => getByText(queryBuilderSetup, 'map'));
    await waitFor(() => getByText(queryBuilderSetup, 'runtime'));

    act(() => {
      queryBuilderState.initialize(
        create_RawLambda(
          TEST_DATA__simpleProjectionWithNonTemporalSourceAndBiTemporalTarget.parameters,
          TEST_DATA__simpleProjectionWithNonTemporalSourceAndBiTemporalTarget.body,
        ),
      );
    });
    const projectionColumnState = guaranteeType(
      queryBuilderState.fetchStructureState.projectionState.columns[0],
      QueryBuilderSimpleProjectionColumnState,
    );
    const derivedPropertyExpressionStates =
      projectionColumnState.propertyExpressionState
        .derivedPropertyExpressionStates;

    // property replaced with derived property as it is milestoned
    expect(derivedPropertyExpressionStates.length).toBe(1);
    const parameterValues = guaranteeNonNullable(
      derivedPropertyExpressionStates[0]?.propertyExpression.parametersValues,
    );

    // default milestoning date is propagated as date propagation is not supported.
    expect(parameterValues.length).toBe(3);
  },
);
Example #18
Source File: task-cancel.ts    From vue-concurrency with MIT License 4 votes vote down vote up
describe("useTask cancel", () => {
  test("taskInstance.cancel results in isCanceled:true and no value", async () => {
    let reached2ndYield = false;
    await mockSetup(async () => {
      const task = useTask(function* () {
        yield wait(15);
        reached2ndYield = true;
        return "foo";
      });
      const taskInstance = task.perform();
      expect(taskInstance.isRunning).toBe(true);
      taskInstance.cancel();

      expect(taskInstance.isCanceling).toBe(true);
      await waitFor(() => expect(taskInstance.isCanceled).toBe(true), {
        interval: 10,
      });
      expect(taskInstance.isFinished).toBe(true);
      expect(taskInstance.isRunning).toBe(false);
      expect(taskInstance.isError).toBe(false);
      expect(taskInstance.isSuccessful).toBe(false);
      expect(taskInstance.value).toBe(null);
      expect(reached2ndYield).toBe(false);
    });
  });

  test("task.cancelAll cancels all running instances", async () => {
    await mockSetup(async () => {
      const task = useTask(function* () {
        return "foo";
      });
      const taskInstance1 = task.perform();
      const taskInstance2 = task.perform();
      try {
        await taskInstance1;
        await taskInstance2;
      } catch (e) {
        expect(e).toBe("cancel");
      }

      const taskInstance3 = task.perform();
      const taskInstance4 = task.perform();

      task.cancelAll();

      expect(taskInstance1.isCanceled).toBe(false);
      expect(taskInstance2.isCanceled).toBe(false);

      await waitFor(() => expect(taskInstance3.isCanceled).toBe(true));
      expect(taskInstance4.isCanceled).toBe(true);
    });
  });

  test("task.cancelAll with force works", async () => {
    await mockSetup(async () => {
      const task = useTask(function* () {
        return "foo";
      });
      
      task.perform();
      task.perform();

      task.cancelAll({ force: true });
    });
  });

  test("signal.pr is called when the task is canceled", async () => {
    const signalCatchCallback = jest.fn();
    await mockSetup(async () => {
      const task = useTask(function* (signal) {
        signal.pr.catch(signalCatchCallback);
        yield wait(30);
        return "foo";
      });
      const taskInstance = task.perform();
      await wait(5);
      taskInstance.cancel();

      await waitFor(() => expect(taskInstance.isFinished).toBe(true));
      expect(signalCatchCallback).toHaveBeenCalled();
    });
  });
});
Example #19
Source File: ModelProvider.test.tsx    From aem-react-editable-components with Apache License 2.0 4 votes vote down vote up
describe('ModelProvider ->', () => {
    const TEST_PAGE_PATH = '/page/jcr:content/root';
    const ROOT_NODE_CLASS_NAME = 'root-class';
    const INNER_COMPONENT_ID = 'innerContent';
    const TEST_COMPONENT_MODEL = { ':type': 'test/components/componentchild' };

    let rootNode: any;

    /**
     * React warn if a non-standard DOM attribute is used on a native DOM node.
     *
     * When the HTML div element is wrapped to be a React Component it is no longer a DOM node and camelCase properties
     * can be passed to props.
     *
     * If instead of the <ModelProvider><Dummy /></ModelProvider> the <ModelProvider><div /></ModelProvider> notation
     * is used, the following error might be shown in the browser console:
     *
     *      Warning: React does not recognize the `camelCaseProp` prop on a DOM element. If you intentionally want it to
     *      appear in the DOM as a custom attribute, spell it as lowercase `camelcaseprop` instead. If you accidentally
     *      passed it from a parent component, remove it from the DOM element.
     *          in div (created by ModelProvider)
     *          in ModelProvider
     *
     * for every camelCase property passed in props.
     *
     * See also: https://github.com/facebook/react/issues/10590
     */
    interface DummyProps extends MappedComponentProperties{
        className: string
    }

    class Dummy extends Component<DummyProps> {
        render() {
            return <div id={INNER_COMPONENT_ID} className={this.props.className}>Dummy</div>;
        }
    }

    let addListenerSpy: jest.SpyInstance;
    let getDataSpy: jest.SpyInstance;

    beforeEach(() => {
        addListenerSpy = jest.spyOn(ModelManager, 'addListener').mockImplementation();
        getDataSpy = jest.spyOn(ModelManager, 'getData').mockResolvedValue(TEST_COMPONENT_MODEL);

        rootNode = document.createElement('div');
        rootNode.className = ROOT_NODE_CLASS_NAME;
        document.body.appendChild(rootNode);
    });

    afterEach(() => {

        if (rootNode) {
            document.body.removeChild(rootNode);
        }
    });

    describe('Tag instantiation ->', () => {
        beforeEach(() => {
            addListenerSpy.mockReset();
        });

        it('should initialize properly without parameter', () => {
            // @ts-expect-error
            ReactDOM.render(<ModelProvider wrappedComponent={Dummy}></ModelProvider>, rootNode);

            expect(addListenerSpy).toHaveBeenCalledWith('', expect.any(Function));

            const childNode = rootNode.querySelector('#' + INNER_COMPONENT_ID);

            expect(childNode).toBeDefined();
        });

        it('should initialize properly with a path parameter', () => {
            ReactDOM.render(<ModelProvider cqPath={TEST_PAGE_PATH} wrappedComponent={Dummy}></ModelProvider>, rootNode);

            expect(addListenerSpy).toHaveBeenCalledWith(TEST_PAGE_PATH, expect.any(Function));

            const childNode = rootNode.querySelector('#' + INNER_COMPONENT_ID);

            expect(childNode).toBeDefined();
        });
    });

    describe('Get data ->', () => {
        beforeEach(() => {
            getDataSpy.mockReset();
            addListenerSpy.mockReset();
        });

        it('should subscribe on the data with undefined parameters', () => {
            getDataSpy.mockResolvedValue({});
            // @ts-expect-error
            ReactDOM.render(<ModelProvider wrappedComponent={Dummy}></ModelProvider>, rootNode);

            expect(addListenerSpy).toHaveBeenCalledWith('', expect.any(Function));
        });

        it('should subscribe on the data with the provided attributes', () => {
            getDataSpy.mockResolvedValue({});
            ReactDOM.render(<ModelProvider cqPath={TEST_PAGE_PATH} cqForceReload={true} wrappedComponent={Dummy}></ModelProvider>, rootNode);

            expect(addListenerSpy).toHaveBeenCalledWith(TEST_PAGE_PATH, expect.any(Function));
        });
    });

    describe('withModel ->', () => {
        beforeEach(() => {
            addListenerSpy.mockReset();
        });

        it('should initialize properly without parameter', () => {
            const DummyWithModel: any = withModel(Dummy);

            ReactDOM.render(<DummyWithModel></DummyWithModel>, rootNode);

            expect(addListenerSpy).toHaveBeenCalledWith('', expect.any(Function));

            const childNode = rootNode.querySelector('#' + INNER_COMPONENT_ID);

            expect(childNode).toBeDefined();
        });

        it('should initialize properly with a path parameter', () => {
            const DummyWithModel = withModel(Dummy);

            // @ts-ignore
            ReactDOM.render(<DummyWithModel cqPath={TEST_PAGE_PATH}></DummyWithModel>, rootNode);

            expect(addListenerSpy).toHaveBeenCalledWith(TEST_PAGE_PATH, expect.any(Function));

            const childNode = rootNode.querySelector('#' + INNER_COMPONENT_ID);

            expect(childNode).toBeDefined();
        });

        it('should render a subpage properly when page path is provided', () => {
            const DummyWithModel = withModel(Dummy, { injectPropsOnInit: true });

            // @ts-ignore
            ReactDOM.render(<DummyWithModel pagePath={TEST_PAGE_PATH}></DummyWithModel>, rootNode);

            expect(getDataSpy).toHaveBeenCalledWith({ path: TEST_PAGE_PATH, forceReload: false });

            const childNode = rootNode.querySelector('#' + INNER_COMPONENT_ID);

            expect(childNode).toBeDefined();
        });

        it('should render components properly when component cqPath is provided', () => {

            const DummyWithModel = withModel(Dummy, { injectPropsOnInit: true });

            // @ts-ignore
            ReactDOM.render(<DummyWithModel cqPath={TEST_PAGE_PATH}></DummyWithModel>, rootNode);

            expect(getDataSpy).toHaveBeenCalledWith({ path: TEST_PAGE_PATH, forceReload: false });

            const childNode = rootNode.querySelector('#' + INNER_COMPONENT_ID);

            expect(childNode).toBeDefined();
        });

        it('should render components properly when containing page path and path to item is provided', () => {
            addListenerSpy = jest.spyOn(ModelManager, 'addListener').mockImplementationOnce((path, callback) => {
                callback();
            });

            const PAGE_PATH = '/page/subpage';
            const ITEM_PATH = 'root/paragraph';

            const DummyWithModel = withModel(Dummy, { injectPropsOnInit: true });

            // @ts-ignore
            ReactDOM.render(<DummyWithModel pagePath={PAGE_PATH} itemPath={ITEM_PATH}></DummyWithModel>, rootNode);

            expect(addListenerSpy).toHaveBeenCalled();
            expect(getDataSpy).toHaveBeenCalledWith({
              path: `${PAGE_PATH}/jcr:content/${ITEM_PATH}`,
              forceReload: false
            });

            const childNode = rootNode.querySelector('#' + INNER_COMPONENT_ID);

            expect(childNode).toBeDefined();
        });

        it('should log error when there is no data', async () => {

            // given
            const error = new Error('404 - Not found');

            getDataSpy.mockRejectedValue(error);

            console.log = jest.fn();

            const DummyWithModel = withModel(Dummy, { injectPropsOnInit: true });

            // when
            // @ts-ignore
            ReactDOM.render(<DummyWithModel cqPath={TEST_PAGE_PATH} ></DummyWithModel>, rootNode);

            // then
            await waitFor(() => expect(console.log).toHaveBeenCalledWith(error));
        });

        it('should fire event to reload editables when in editor', async () => {
            const dispatchEventSpy: jest.SpyInstance =
                jest.spyOn(PathUtils, 'dispatchGlobalCustomEvent').mockImplementation();
            const isInEditor:jest.SpyInstance = jest.spyOn(Utils, 'isInEditor').mockImplementation(() => true);

            const DummyWithModel = withModel(Dummy, { injectPropsOnInit: true });

            // @ts-ignore
            ReactDOM.render(<DummyWithModel pagePath={TEST_PAGE_PATH}></DummyWithModel>, rootNode);

            expect(getDataSpy).toHaveBeenCalledWith({ path: TEST_PAGE_PATH, forceReload: false });

            const childNode = rootNode.querySelector('#' + INNER_COMPONENT_ID);

            expect(childNode).toBeDefined();

            await waitFor(() =>
                expect(dispatchEventSpy).toHaveBeenCalledWith(Constants.ASYNC_CONTENT_LOADED_EVENT, {})
            );

            isInEditor.mockReset();
            dispatchEventSpy.mockReset();
        });
    });

    describe('Unmount -> ', () => {
        it('should remove listeners on unmount', () => {
            const removeListenerSpy: jest.SpyInstance = jest.spyOn(ModelManager, 'removeListener').mockImplementation();

            ReactDOM.render(<ModelProvider cqPath={TEST_PAGE_PATH} wrappedComponent={Dummy}></ModelProvider>, rootNode);

            ReactDOM.unmountComponentAtNode(rootNode);
            expect(removeListenerSpy).toHaveBeenCalledWith(TEST_PAGE_PATH, expect.any(Function));
        });
    });
});
Example #20
Source File: useAsyncResource.test.ts    From use-async-resource with ISC License 4 votes vote down vote up
describe('useAsyncResource', () => {
  const apiFn = (id: number) => Promise.resolve({ id, name: 'test name' });
  const apiSimpleFn = () => Promise.resolve({ message: 'success' });
  const apiFailingFn = () => Promise.reject({ message: 'error' });

  afterEach(() => {
    resourceCache(apiFn).clear();
    resourceCache(apiSimpleFn).clear();
    resourceCache(apiFailingFn).clear();
  });

  it('should create a new data reader', async () => {
    // get the data reader from the custom hook, with params
    const { result } = renderHook(() => useAsyncResource(apiFn, 1));
    const [dataReader] = result.current;

    // wait for it to fulfill
    await suspendFor(dataReader);

    // should be able to get raw data from the data reader
    expect(dataReader()).toStrictEqual({ id: 1, name: 'test name' });

    // same for api functions without params
    const { result: simpleResult } = renderHook(() => useAsyncResource(apiSimpleFn, []));
    const [simpleData] = simpleResult.current;
    await suspendFor(simpleData);
    expect(simpleData()).toStrictEqual({ message: 'success' });
  });

  it('should throw an error', async () => {
    const { result } = renderHook(() => useAsyncResource(apiFailingFn, []));
    const [dataReader] = result.current;

    // wait for it to fulfill
    await suspendFor(dataReader);

    expect(dataReader).toThrowError(Error('error'));
  });

  it('should trigger an update for the data reader', async () => {
    // get the data reader and the updater function from the custom hook
    const { result } = renderHook(() => useAsyncResource(apiFn, 1));
    const [dataReader, updateDataReader] = result.current;

    // wait for it to fulfill
    await suspendFor(dataReader);

    // make sure we're able to get raw data from it
    expect(dataReader(u => u.id)).toStrictEqual(1);

    // call the updater function with new params
    act(() => updateDataReader(2));

    // this should generate a brand new data reader
    const [newDataReader] = result.current;
    // we will need to wait for its fulfillment
    await suspendFor(newDataReader);

    // check that it's indeed a new one
    expect(newDataReader).not.toStrictEqual(dataReader);
    // and that it returns different data
    expect(newDataReader(u => u.id)).toStrictEqual(2);
  });

  it('should reuse a cached data reader', async () => {
    // get the data reader and the updater function from the custom hook
    const { result } = renderHook(() => useAsyncResource(apiFn, 1));
    const [dataReader, updateDataReader] = result.current;

    // wait for it to fulfill
    await suspendFor(dataReader);

    // call the updater function with new params
    act(() => updateDataReader(2));

    // this should generate a brand new data reader
    const [newDataReader] = result.current;
    // we will need to wait for its fulfillment
    await suspendFor(newDataReader);

    // call the updater one more time, but with the previous param
    act(() => updateDataReader(1));

    // the new data reader should use the previously cached version
    const [cachedDataReader] = result.current;
    // so nothing to wait for
    expect(cachedDataReader).not.toThrow();

    // make sure it's the exact same as the very first one
    expect(cachedDataReader).toStrictEqual(dataReader);
    // and that it returns the same data
    expect(cachedDataReader(u => u.id)).toStrictEqual(1);
  });

  it('should create a lazy data reader', async () => {
    // initialize a lazy data reader
    const { result } = renderHook(() => useAsyncResource(apiFn));
    const [dataReader, updateDataReader] = result.current;

    // it should be available immediately, but should return `undefined`
    expect(dataReader).not.toThrow();
    expect(dataReader()).toStrictEqual(undefined);

    // triggering an api call
    act(() => updateDataReader(1));
    const [updatedDataReader] = result.current;

    // requires waiting for a fulfillment
    await suspendFor(updatedDataReader);

    // finally, we should have some data available
    expect(updatedDataReader(u => u.id)).toStrictEqual(1);
  });

  it('should call the api function again if the cache is cleared', async () => {
    // get the data reader and the updater function from the custom hook
    const { result } = renderHook(() => useAsyncResource(apiFn, 1));
    const [dataReader, updateDataReader] = result.current;
    await suspendFor(dataReader);

    // clear the cache before calling the updater with the previous param
    resourceCache(apiFn).delete(1);

    // call the updater function with same params
    act(() => updateDataReader(1));
    // this should generate a brand new data reader
    const [newDataReader] = result.current;
    // and we will need to wait for its fulfillment
    await suspendFor(newDataReader);

    // make sure it's different than the first one
    expect(newDataReader).not.toStrictEqual(dataReader);
    // but that it returns the same data
    expect(newDataReader(u => u.id)).toStrictEqual(1);
  });

  it('should trigger new api calls if the params of the hook change', async () => {
    // get the data reader and the updater function, injecting a prop that we'll update later
    const { result, rerender } = renderHook(
      ({ paramId }) => useAsyncResource(apiFn, paramId),
      { initialProps: { paramId: 1 }},
    );

    // check that it suspends and it resolves with the expected data
    let [dataReader] = result.current;
    await suspendFor(dataReader);
    expect(dataReader()).toStrictEqual({ id: 1, name: 'test name' });

    // re-render with new props
    rerender({ paramId: 2 });

    // check that it suspends again and renders with new data
    const [newDataReader] = result.current;
    await suspendFor(newDataReader);
    expect(newDataReader()).toStrictEqual({ id: 2, name: 'test name' });
  });

  it('should persist the data reader between renders - for api function with params', async () => {
    // get the data reader and the updater function
    const { result, rerender } = renderHook(
      ({ paramId }) => useAsyncResource(apiFn, paramId),
      { initialProps: { paramId: 1 }},
    );

    // check that it suspends and it resolves with the expected data
    let [dataReader] = result.current;
    await suspendFor(dataReader);
    expect(dataReader()).toStrictEqual({ id: 1, name: 'test name' });

    // re-render with same props
    rerender({ paramId: 1 });

    // check that it doesn't suspend again and the data reader is reused
    const [newDataReader] = result.current;
    expect(newDataReader).toStrictEqual(dataReader);
  });

  it('should persist the data reader between renders - for api function without params', async () => {
    // get the data reader and the updater function
    const { result, rerender } = renderHook(() => useAsyncResource(apiSimpleFn, []));

    // check that it suspends and it resolves with the expected data
    let [dataReader] = result.current;
    await suspendFor(dataReader);
    expect(dataReader()).toStrictEqual({ message: 'success' });

    // render again
    rerender();

    // check that it doesn't suspend again and the data reader is reused
    const [newDataReader] = result.current;
    expect(newDataReader()).toStrictEqual(dataReader());
    // expect(newDataReader).toStrictEqual(dataReader);
  });

  it('should preload a resource before rendering', async () => {
    // start preloading the resource
    preloadResource(apiSimpleFn);

    // expect the resource to load faster than the component that will consume it
    const preloadedResource = resourceCache(apiSimpleFn).get();
    if (preloadedResource) {
      await waitFor(() => preloadedResource());
    }

    // a component consuming the preloaded resource should have the data readily available
    const { result } = renderHook(() => useAsyncResource(apiSimpleFn, []));
    let [dataReader] = result.current;
    expect(dataReader()).toStrictEqual({ message: 'success' });
  });
});
Example #21
Source File: authorizer.test.tsx    From hub with Apache License 2.0 4 votes vote down vote up
describe('authorizer', () => {
  afterEach(() => {
    jest.resetAllMocks();
  });

  it("does't call to getUserAllowedActions when selectedOrg is undefined", async () => {
    authorizer.init();

    await waitFor(() => {
      expect(API.getUserAllowedActions).toHaveBeenCalledTimes(0);
    });
  });

  it('calls to getUserAllowedActions when selectedOrg is defined', async () => {
    mocked(API).getUserAllowedActions.mockResolvedValue(allActions);
    authorizer.init('org1');

    await waitFor(() => {
      expect(API.getUserAllowedActions).toHaveBeenCalledTimes(1);
      expect(API.getUserAllowedActions).toHaveBeenCalledWith('org1');
    });
  });

  it('calls onCompletion', async () => {
    mocked(API).getUserAllowedActions.mockResolvedValue(allActions);
    authorizer.init('org1');
    authorizer.getAllowedActionsList(onCompletionMock);

    await waitFor(() => {
      expect(API.getUserAllowedActions).toHaveBeenCalledTimes(2);
      expect(API.getUserAllowedActions).toHaveBeenLastCalledWith('org1');
      expect(onCompletionMock).toHaveBeenCalledTimes(1);
    });
  });

  describe('to update context', () => {
    it('calls again to getUserAllowedActions when a new org is defined', async () => {
      mocked(API).getUserAllowedActions.mockResolvedValue(allActions);
      authorizer.init('org1');

      await waitFor(() => {
        expect(API.getUserAllowedActions).toHaveBeenCalledWith('org1');
      });

      authorizer.updateCtx('org2');

      await waitFor(() => {
        expect(API.getUserAllowedActions).toHaveBeenCalledTimes(2);
        expect(API.getUserAllowedActions).toHaveBeenLastCalledWith('org2');
      });
    });

    it('does not call getUserAllowedActions again when selected org is the same', async () => {
      mocked(API).getUserAllowedActions.mockResolvedValue(allActions);
      authorizer.init('org1');

      await waitFor(() => {
        expect(API.getUserAllowedActions).toHaveBeenCalledWith('org1');
      });

      authorizer.updateCtx('org1');

      await waitFor(() => {
        expect(API.getUserAllowedActions).toHaveBeenCalledTimes(1);
      });
    });

    it('does not call getUserAllowedActions when not selected org', async () => {
      mocked(API).getUserAllowedActions.mockResolvedValue(allActions);
      authorizer.init('org1');

      await waitFor(() => {
        expect(API.getUserAllowedActions).toHaveBeenCalledWith('org1');
      });

      authorizer.updateCtx();

      await waitFor(() => {
        expect(API.getUserAllowedActions).toHaveBeenCalledTimes(1);
      });
    });

    it('calls only one getUserAllowedActions when a new org is selected and previously not selected org', async () => {
      mocked(API).getUserAllowedActions.mockResolvedValue(allActions);
      authorizer.init();
      authorizer.updateCtx('org1');

      await waitFor(() => {
        expect(API.getUserAllowedActions).toHaveBeenCalledTimes(1);
        expect(API.getUserAllowedActions).toHaveBeenCalledWith('org1');
      });
    });
  });

  describe('check authorizated actions', () => {
    it('calls getUserAllowedActions when selected org is different to saved one', async () => {
      mocked(API).getUserAllowedActions.mockResolvedValue(allActions);
      authorizer.init('org1');

      await waitFor(() => {
        expect(API.getUserAllowedActions).toHaveBeenCalledTimes(1);
        expect(API.getUserAllowedActions).toHaveBeenCalledWith('org1');
      });

      authorizer.check({
        action: AuthorizerAction.DeleteOrganization,
        organizationName: 'org2',
        user: 'user1',
      });

      await waitFor(() => {
        expect(API.getUserAllowedActions).toHaveBeenCalledTimes(2);
        expect(API.getUserAllowedActions).toHaveBeenLastCalledWith('org2');
      });
    });

    it('returns true when all actions are allowed', async () => {
      mocked(API).getUserAllowedActions.mockResolvedValue(allActions);
      authorizer.init('org1');

      await waitFor(() => {
        expect(API.getUserAllowedActions).toHaveBeenCalledTimes(1);
        expect(API.getUserAllowedActions).toHaveBeenCalledWith('org1');
      });

      const checked = authorizer.check({
        action: AuthorizerAction.DeleteOrganization,
        user: 'user1',
      });

      expect(checked).toBeTruthy();
    });

    it('returns true when action is allowed', async () => {});
    it('returns false when action is not allowed', async () => {});
  });
});
Example #22
Source File: dxc-date-input.component.spec.ts    From halstack-angular with Apache License 2.0 4 votes vote down vote up
describe("DxcDate", () => {
  const newMockDate = new Date("1995/12/03");
  const newValue = "03-12-1995";

  test("should render dxc-date", async () => {
    const { getByText } = await render(DxcDateInputComponent, {
      componentProperties: { label: "test-date" },
      imports: [
        MatDayjsDateModule,
        MatNativeDateModule,
        MatInputModule,
        MatDatepickerModule,
        MdePopoverModule,
        DxcBoxModule,
        CommonModule,
        DxcTextInputModule,
      ],
      providers: [
        { provide: MAT_DATE_FORMATS, useValue: DAYJS_DATE_FORMATS },
        {
          provide: DateAdapter,
          useClass: DayjsDateAdapter,
          deps: [MAT_DATE_LOCALE, Platform],
        },
      ],
    });

    expect(getByText("test-date"));
  });

  test("The input´s value is the same as the one received as a parameter", async () => {
    const onChange = jest.fn();
    const onBlur = jest.fn();

    await render(DxcDateInputComponent, {
      componentProperties: {
        label: "test-input",
        value: "03-12-1995",
        onChange: {
          emit: onChange,
        } as any,
        onBlur: {
          emit: onBlur,
        } as any,
      },
      imports: [
        MatDayjsDateModule,
        MatNativeDateModule,
        MatInputModule,
        MatDatepickerModule,
        MdePopoverModule,
        DxcBoxModule,
        CommonModule,
        DxcTextInputModule,
      ],
      providers: [
        { provide: MAT_DATE_FORMATS, useValue: DAYJS_DATE_FORMATS },
        {
          provide: DateAdapter,
          useClass: DayjsDateAdapter,
          deps: [MAT_DATE_LOCALE, Platform],
        },
      ],
    });

    const input = <HTMLInputElement>screen.getByRole("textbox");
    const calendarIcon = screen.getByRole("calendarIcon");

    input.focus();
    expect(screen.getByDisplayValue("03-12-1995"));
    fireEvent.click(calendarIcon);
    expect(screen.getByText("DECEMBER 1995"));
    expect(
      screen.getByText("3").classList.contains("mat-calendar-body-selected")
    ).toBeTruthy();
  });

  test("dxc-date value change and default format", async () => {
    const onChange = jest.fn();

    await render(DxcDateInputComponent, {
      componentProperties: {
        label: "test-input",
        onChange: {
          emit: onChange,
        } as any,
      },
      imports: [
        MatDayjsDateModule,
        MatNativeDateModule,
        MatInputModule,
        MatDatepickerModule,
        MdePopoverModule,
        DxcBoxModule,
        CommonModule,
        DxcTextInputModule,
      ],
      providers: [
        { provide: MAT_DATE_FORMATS, useValue: DAYJS_DATE_FORMATS },
        {
          provide: DateAdapter,
          useClass: DayjsDateAdapter,
          deps: [MAT_DATE_LOCALE, Platform],
        },
      ],
    });

    const input = <HTMLInputElement>screen.getByRole("textbox");
    input.focus();
    fireEvent.input(input, { target: { value: newValue } });
    expect(onChange).toHaveBeenCalledWith({
      value: newValue,
      error: undefined,
      date: newMockDate,
    });
    expect(screen.getByDisplayValue(newValue));
  });

  test("dxc-date change value twice as uncontrolled", async () => {
    const onChange = jest.fn();

    await render(DxcDateInputComponent, {
      componentProperties: {
        label: "test-input",
        defaultValue: "22-10-1998",
        onChange: {
          emit: onChange,
        } as any,
      },
      imports: [
        MatDayjsDateModule,
        MatNativeDateModule,
        MatInputModule,
        MatDatepickerModule,
        MdePopoverModule,
        DxcBoxModule,
        CommonModule,
        DxcTextInputModule,
      ],
      providers: [
        { provide: MAT_DATE_FORMATS, useValue: DAYJS_DATE_FORMATS },
        {
          provide: DateAdapter,
          useClass: DayjsDateAdapter,
          deps: [MAT_DATE_LOCALE, Platform],
        },
      ],
    });
    expect(screen.getByDisplayValue("22-10-1998"));
    const input = <HTMLInputElement>screen.getByRole("textbox");
    input.focus();
    fireEvent.input(input, { target: { value: newValue } });
    expect(onChange).toHaveBeenCalledWith({
      value: newValue,
      error: undefined,
      date: newMockDate,
    });
    expect(screen.getByDisplayValue(newValue));

    input.focus();
    fireEvent.input(input, { target: { value: "04-10-1996" } });
    expect(onChange).toHaveBeenCalledWith({
      value: "04-10-1996",
      error: undefined,
      date: new Date("1996/10/04"),
    });
    expect(screen.getByDisplayValue("04-10-1996"));
  });

  test("Calendar´s value is the same as the input´s date if it´s right (Depending on the format)", async () => {
    const onChange = jest.fn();
    const onBlur = jest.fn();

    await render(DxcDateInputComponent, {
      componentProperties: {
        label: "test-input",
        format: "YYYY/MM/DD",
        onChange: {
          emit: onChange,
        } as any,
        onBlur: {
          emit: onBlur,
        } as any,
      },
      imports: [
        MatDayjsDateModule,
        MatNativeDateModule,
        MatInputModule,
        MatDatepickerModule,
        MdePopoverModule,
        DxcBoxModule,
        CommonModule,
        DxcTextInputModule,
      ],
      providers: [
        { provide: MAT_DATE_FORMATS, useValue: DAYJS_DATE_FORMATS },
        {
          provide: DateAdapter,
          useClass: DayjsDateAdapter,
          deps: [MAT_DATE_LOCALE, Platform],
        },
      ],
    });

    const input = <HTMLInputElement>screen.getByRole("textbox");
    const calendarIcon = screen.getByRole("calendarIcon");

    input.focus();
    fireEvent.input(input, { target: { value: "1995/12/03" } });
    expect(onChange).toHaveBeenCalledWith({
      value: "1995/12/03",
      error: undefined,
      date: newMockDate,
    });
    input.focus();
    expect(screen.getByDisplayValue("1995/12/03"));
    fireEvent.click(calendarIcon);
    waitFor(() => {
      expect(screen.getByText("DECEMBER 1995"));
    });
    waitFor(() => {
      expect(
        screen.getByText("3").classList.contains("mat-calendar-body-selected")
      ).toBeTruthy();
    });
  });

  test("dxc-date invalid value", async () => {
    const onChange = jest.fn();
    const invalidValue = "03-12-199_";

    await render(DxcDateInputComponent, {
      componentProperties: {
        label: "test-input",
        onChange: {
          emit: onChange,
        } as any,
      },
      imports: [
        MatDayjsDateModule,
        MatNativeDateModule,
        MatInputModule,
        MatDatepickerModule,
        MdePopoverModule,
        DxcBoxModule,
        CommonModule,
        DxcTextInputModule,
      ],
      providers: [
        { provide: MAT_DATE_FORMATS, useValue: DAYJS_DATE_FORMATS },
        {
          provide: DateAdapter,
          useClass: DayjsDateAdapter,
          deps: [MAT_DATE_LOCALE, Platform],
        },
      ],
    });
    const input = <HTMLInputElement>screen.getByRole("textbox");

    input.focus();
    fireEvent.input(input, { target: { value: invalidValue } });

    expect(onChange).toHaveBeenCalledWith({
      value: invalidValue,
      error: undefined,
      date: undefined,
    });
  });

  test("onChange function is called when the user selects from the calendar", async () => {
    const onChange = jest.fn();

    await render(DxcDateInputComponent, {
      componentProperties: {
        label: "test-input",
        value: newValue,
        onChange: {
          emit: onChange,
        } as any,
      },
      imports: [
        MatDayjsDateModule,
        MatNativeDateModule,
        MatInputModule,
        MatDatepickerModule,
        MdePopoverModule,
        DxcBoxModule,
        CommonModule,
        DxcTextInputModule,
      ],
      providers: [
        { provide: MAT_DATE_FORMATS, useValue: DAYJS_DATE_FORMATS },
        {
          provide: DateAdapter,
          useClass: DayjsDateAdapter,
          deps: [MAT_DATE_LOCALE, Platform],
        },
      ],
    });
    const calendarIcon = screen.getByRole("calendarIcon");

    fireEvent.click(calendarIcon);
    fireEvent.click(screen.getByText("4"));
    waitFor(() => {
      expect(onChange).toHaveBeenCalledWith({
        value: "04-12-1995",
        date: new Date("04/12/1995"),
      });
    });
  });

  test("onChange function is called when the user selects from the calendar, the stringValue received by the function is the date with the correct format", async () => {
    const onChange = jest.fn();

    await render(DxcDateInputComponent, {
      componentProperties: {
        label: "test-input",
        value: "12-03-1995",
        format: "MM-DD-YYYY",
        onChange: {
          emit: onChange,
        } as any,
      },
      imports: [
        MatDayjsDateModule,
        MatNativeDateModule,
        MatInputModule,
        MatDatepickerModule,
        MdePopoverModule,
        DxcBoxModule,
        CommonModule,
        DxcTextInputModule,
      ],
      providers: [
        { provide: MAT_DATE_FORMATS, useValue: DAYJS_DATE_FORMATS },
        {
          provide: DateAdapter,
          useClass: DayjsDateAdapter,
          deps: [MAT_DATE_LOCALE, Platform],
        },
      ],
    });
    const calendarIcon = screen.getByRole("calendarIcon");

    fireEvent.click(calendarIcon);
    expect(screen.getByText("DECEMBER 1995"));
    expect(
      screen.getByText("3").classList.contains("mat-calendar-body-selected")
    ).toBeTruthy();
    fireEvent.click(screen.getByText("4"));
    waitFor(() => {
      expect(onChange).toHaveBeenCalledWith({
        value: "12-04-1995",
        date: new Date("04/12/1995"),
      });
    });
  });

  test("If the user types something, if controlled and without onChange, the value doesn´t change", async () => {
    const onChange = jest.fn();

    await render(DxcDateInputComponent, {
      componentProperties: {
        label: "test-input",
        value: newValue,
        onChange: {
          emit: onChange,
        } as any,
      },
      imports: [
        MatDayjsDateModule,
        MatNativeDateModule,
        MatInputModule,
        MatDatepickerModule,
        MdePopoverModule,
        DxcBoxModule,
        CommonModule,
        DxcTextInputModule,
      ],
      providers: [
        { provide: MAT_DATE_FORMATS, useValue: DAYJS_DATE_FORMATS },
        {
          provide: DateAdapter,
          useClass: DayjsDateAdapter,
          deps: [MAT_DATE_LOCALE, Platform],
        },
      ],
    });
    const calendarIcon = screen.getByRole("calendarIcon");

    fireEvent.click(calendarIcon);
    expect(screen.getByText("DECEMBER 1995"));
    expect(
      screen.getByText("3").classList.contains("mat-calendar-body-selected")
    ).toBeTruthy();
    fireEvent.click(screen.getByText("4"));
    waitFor(() => {
      expect(onChange).toHaveBeenCalledWith({
        value: "04-12-1995",
        date: new Date("04/12/1995"),
      });
    });

    fireEvent.click(calendarIcon);

    expect(screen.getByText("DECEMBER 1995"));
    waitFor(() => {
      expect(
        screen.getByText("3").classList.contains("mat-calendar-body-selected")
      ).toBeTruthy();
    });
  });

  test("controlled dxc-date", async () => {
    const onChange = jest.fn();
    const onBlur = jest.fn();

    await render(DxcDateInputComponent, {
      componentProperties: {
        label: "test-input",
        value: "03-12-1995",
        defaultValue: "12-04-1995",
        onChange: {
          emit: onChange,
        } as any,
        onBlur: {
          emit: onBlur,
        } as any,
      },
      imports: [
        MatDayjsDateModule,
        MatNativeDateModule,
        MatInputModule,
        MatDatepickerModule,
        MdePopoverModule,
        DxcBoxModule,
        CommonModule,
        DxcTextInputModule,
      ],
      providers: [
        { provide: MAT_DATE_FORMATS, useValue: DAYJS_DATE_FORMATS },
        {
          provide: DateAdapter,
          useClass: DayjsDateAdapter,
          deps: [MAT_DATE_LOCALE, Platform],
        },
      ],
    });
    expect(screen.getByDisplayValue("03-12-1995"));
    const input = <HTMLInputElement>screen.getByRole("textbox");

    input.focus();
    fireEvent.input(input, { target: { value: "03-10-1996" } });
    expect(onChange).toHaveBeenCalledWith({
      value: "03-10-1996",
      error: undefined,
      date: new Date("1996/10/03"),
    });
    expect(screen.getByDisplayValue("03-12-1995"));
    fireEvent.blur(input);
    waitFor(() => {
      expect(onBlur).toHaveBeenCalledWith({
        error: undefined,
        value: "03-12-1995",
        date: new Date("1995/12/03"),
      });
    });
  });
});
Example #23
Source File: QueryBuilder_FetchStructure.test.tsx    From legend-studio with Apache License 2.0 4 votes vote down vote up
test(
  integrationTest(
    'Query builder state is properly set after processing a projection lambda',
  ),
  async () => {
    const pluginManager = LegendQueryPluginManager.create();
    pluginManager.usePresets([new Query_GraphPreset()]).install();
    const mockedQueryStore = TEST__provideMockedLegendQueryStore({
      pluginManager,
    });
    const renderResult = await TEST__setUpQueryEditor(
      mockedQueryStore,
      TEST_DATA__ComplexRelationalModel,
      stub_RawLambda(),
      'model::relational::tests::simpleRelationalMapping',
      'model::MyRuntime',
    );
    const queryBuilderState = mockedQueryStore.queryBuilderState;

    const _personClass = mockedQueryStore.graphManagerState.graph.getClass(
      'model::pure::tests::model::simple::Person',
    );
    const _firmClass = mockedQueryStore.graphManagerState.graph.getClass(
      'model::pure::tests::model::simple::Firm',
    );
    const mapping = mockedQueryStore.graphManagerState.graph.getMapping(
      'model::relational::tests::simpleRelationalMapping',
    );

    act(() => {
      queryBuilderState.changeClass(_personClass);
    });
    const queryBuilderSetup = await waitFor(() =>
      renderResult.getByTestId(QUERY_BUILDER_TEST_ID.QUERY_BUILDER_SETUP),
    );
    await waitFor(() => getByText(queryBuilderSetup, 'Person'));
    await waitFor(() =>
      getByText(queryBuilderSetup, 'simpleRelationalMapping'),
    );
    await waitFor(() => getByText(queryBuilderSetup, 'MyRuntime'));
    const treeData = guaranteeNonNullable(
      queryBuilderState.explorerState.treeData,
    );
    const rootNode = guaranteeType(
      treeData.nodes.get(treeData.rootIds[0] as string),
      QueryBuilderExplorerTreeRootNodeData,
    );

    expect(getRootSetImplementation(mapping, _personClass)).toBe(
      rootNode.mappingData.targetSetImpl,
    );
    expect(rootNode.mappingData.mapped).toBe(true);

    // simpleProjection
    act(() => {
      queryBuilderState.initialize(
        create_RawLambda(
          TEST_DATA__simpleProjection.parameters,
          TEST_DATA__simpleProjection.body,
        ),
      );
    });
    let projectionCols = await waitFor(() =>
      renderResult.getByTestId(QUERY_BUILDER_TEST_ID.QUERY_BUILDER_PROJECTION),
    );
    const FIRST_NAME_ALIAS = 'Edited First Name';
    const LAST_NAME_ALIAS = 'Last Name';
    expect(
      await waitFor(() =>
        projectionCols.querySelector(`input[value="${FIRST_NAME_ALIAS}"]`),
      ),
    ).not.toBeNull();
    expect(
      await waitFor(() =>
        projectionCols.querySelector(`input[value="${LAST_NAME_ALIAS}"]`),
      ),
    ).not.toBeNull();
    expect(
      queryBuilderState.fetchStructureState.projectionState.columns.length,
    ).toBe(2);
    let fistNameCol = guaranteeNonNullable(
      queryBuilderState.fetchStructureState.projectionState.columns.find(
        (e) => e.columnName === FIRST_NAME_ALIAS,
      ),
    );
    const firstNameProperty = guaranteeType(
      fistNameCol,
      QueryBuilderSimpleProjectionColumnState,
    ).propertyExpressionState.propertyExpression.func;
    expect(firstNameProperty).toBe(getClassProperty(_personClass, 'firstName'));
    const lastNameCol = guaranteeNonNullable(
      queryBuilderState.fetchStructureState.projectionState.columns.find(
        (e) => e.columnName === LAST_NAME_ALIAS,
      ),
    );
    const lastNameProperty = guaranteeType(
      lastNameCol,
      QueryBuilderSimpleProjectionColumnState,
    ).propertyExpressionState.propertyExpression.func;
    expect(lastNameProperty).toBe(getClassProperty(_personClass, 'lastName'));
    expect(queryBuilderState.resultSetModifierState.limit).toBeUndefined();

    // chainedProperty
    const CHAINED_PROPERTY_ALIAS = 'Firm/Legal Name';
    act(() => {
      queryBuilderState.initialize(
        create_RawLambda(
          TEST_DATA__projectionWithChainedProperty.parameters,
          TEST_DATA__projectionWithChainedProperty.body,
        ),
      );
    });
    const projectionWithChainedPropertyCols = await waitFor(() =>
      renderResult.getByTestId(QUERY_BUILDER_TEST_ID.QUERY_BUILDER_PROJECTION),
    );
    expect(
      await waitFor(() =>
        projectionWithChainedPropertyCols.querySelector(
          `input[value="${CHAINED_PROPERTY_ALIAS}"]`,
        ),
      ),
    ).not.toBeNull();
    expect(
      queryBuilderState.fetchStructureState.projectionState.columns.length,
    ).toBe(1);
    let legalNameCol = guaranteeNonNullable(
      queryBuilderState.fetchStructureState.projectionState.columns.find(
        (e) => e.columnName === CHAINED_PROPERTY_ALIAS,
      ),
    );
    const legalNameColProperty = guaranteeType(
      legalNameCol,
      QueryBuilderSimpleProjectionColumnState,
    ).propertyExpressionState.propertyExpression.func;
    expect(legalNameColProperty).toBe(
      getClassProperty(_firmClass, 'legalName'),
    );
    const _firmPropertyExpression = guaranteeType(
      guaranteeType(legalNameCol, QueryBuilderSimpleProjectionColumnState)
        .propertyExpressionState.propertyExpression.parametersValues[0],
      AbstractPropertyExpression,
    );
    expect(_firmPropertyExpression.func).toBe(
      getClassProperty(_personClass, 'firm'),
    );
    expect(queryBuilderState.resultSetModifierState.limit).toBeUndefined();

    // result set modifiers
    const RESULT_LIMIT = 500;
    act(() => {
      queryBuilderState.initialize(
        create_RawLambda(
          TEST_DATA__projectionWithResultSetModifiers.parameters,
          TEST_DATA__projectionWithResultSetModifiers.body,
        ),
      );
    });
    projectionCols = await waitFor(() =>
      renderResult.getByTestId(QUERY_BUILDER_TEST_ID.QUERY_BUILDER_PROJECTION),
    );
    expect(
      await waitFor(() =>
        projectionCols.querySelector(`input[value="${FIRST_NAME_ALIAS}"]`),
      ),
    ).not.toBeNull();
    expect(
      await waitFor(() =>
        projectionCols.querySelector(`input[value="${LAST_NAME_ALIAS}"]`),
      ),
    ).not.toBeNull();
    expect(
      await waitFor(() =>
        projectionCols.querySelector(
          `input[value="${CHAINED_PROPERTY_ALIAS}"]`,
        ),
      ),
    ).not.toBeNull();
    expect(
      queryBuilderState.fetchStructureState.projectionState.columns.length,
    ).toBe(3);
    const resultSetModifierState = queryBuilderState.resultSetModifierState;
    expect(resultSetModifierState.limit).toBe(RESULT_LIMIT);
    expect(resultSetModifierState.distinct).toBe(true);
    expect(resultSetModifierState.sortColumns).toHaveLength(2);
    fistNameCol = guaranteeNonNullable(
      queryBuilderState.fetchStructureState.projectionState.columns.find(
        (e) => e.columnName === FIRST_NAME_ALIAS,
      ),
    );
    legalNameCol = guaranteeNonNullable(
      queryBuilderState.fetchStructureState.projectionState.columns.find(
        (e) => e.columnName === CHAINED_PROPERTY_ALIAS,
      ),
    );
    const firstnameSortState = guaranteeNonNullable(
      resultSetModifierState.sortColumns.find(
        (e) => e.columnState === fistNameCol,
      ),
    );
    expect(firstnameSortState.sortType).toBe(COLUMN_SORT_TYPE.ASC);
    const legalNameColSortState = guaranteeNonNullable(
      resultSetModifierState.sortColumns.find(
        (e) => e.columnState === legalNameCol,
      ),
    );
    expect(legalNameColSortState.sortType).toBe(COLUMN_SORT_TYPE.DESC);
    const queryBuilder = await waitFor(() =>
      renderResult.getByTestId(QUERY_BUILDER_TEST_ID.QUERY_BUILDER),
    );
    fireEvent.click(
      getByTitle(queryBuilder, 'Configure result set modifiers...'),
    );
    const modal = await waitFor(() => renderResult.getByRole('dialog'));
    await waitFor(() => getByText(modal, 'Sort and Order'));
    await waitFor(() => getByText(modal, 'Eliminate Duplicate Rows'));
    await waitFor(() => getByText(modal, 'Limit Results'));
    await waitFor(() => getByText(modal, FIRST_NAME_ALIAS));
    await waitFor(() => getByText(modal, CHAINED_PROPERTY_ALIAS));
    expect(
      await waitFor(() =>
        modal.querySelector(`input[value="${RESULT_LIMIT}"]`),
      ),
    ).not.toBeNull();
    fireEvent.click(getByText(modal, 'Close'));

    // filter with simple condition
    await waitFor(() => renderResult.getByText('Add a filter condition'));
    act(() => {
      queryBuilderState.initialize(
        create_RawLambda(
          TEST_DATA__getAllWithOneConditionFilter.parameters,
          TEST_DATA__getAllWithOneConditionFilter.body,
        ),
      );
    });
    let filterValue = 'testFirstName';
    let filterPanel = await waitFor(() =>
      renderResult.getByTestId(QUERY_BUILDER_TEST_ID.QUERY_BUILDER_FILTER),
    );
    expect(
      await waitFor(() =>
        filterPanel.querySelector(`input[value="${filterValue}"]`),
      ),
    ).not.toBeNull();
    await waitFor(() => getByText(filterPanel, 'First Name'));
    await waitFor(() => getByText(filterPanel, 'is'));
    const filterState = queryBuilderState.filterState;
    expect(filterState.nodes.size).toBe(1);
    expect(
      queryBuilderState.fetchStructureState.projectionState.columns.length,
    ).toBe(0);

    // filter with group condition
    act(() => {
      queryBuilderState.resetQueryBuilder();
      queryBuilderState.resetQuerySetup();
    });
    await waitFor(() => renderResult.getByText('Add a filter condition'));
    act(() => {
      queryBuilderState.initialize(
        create_RawLambda(
          TEST_DATA__getAllWithGroupedFilter.parameters,
          TEST_DATA__getAllWithGroupedFilter.body,
        ),
      );
    });
    filterPanel = await waitFor(() =>
      renderResult.getByTestId(QUERY_BUILDER_TEST_ID.QUERY_BUILDER_FILTER),
    );
    await waitFor(() =>
      expect(getAllByText(filterPanel, 'is')).toHaveLength(2),
    );
    await waitFor(() => getByText(filterPanel, 'or'));
    filterValue = 'lastNameTest';
    expect(
      await waitFor(() =>
        filterPanel.querySelector(`input[value="${filterValue}"]`),
      ),
    ).not.toBeNull();
    await waitFor(() => getByText(filterPanel, 'First Name'));
    const lastNameFilterValue = 'lastNameTest';
    expect(
      await waitFor(() =>
        filterPanel.querySelector(`input[value="${lastNameFilterValue}"]`),
      ),
    ).not.toBeNull();
    await waitFor(() => getByText(filterPanel, 'Last Name'));
    expect(queryBuilderState.filterState.nodes.size).toBe(3);
    expect(
      queryBuilderState.fetchStructureState.projectionState.columns.length,
    ).toBe(0);

    // projection column with derived property
    act(() => {
      queryBuilderState.resetQueryBuilder();
      queryBuilderState.resetQuerySetup();
    });
    await waitFor(() => renderResult.getByText('Add a filter condition'));
    act(() => {
      queryBuilderState.initialize(
        create_RawLambda(
          TEST_DATA__projectWithDerivedProperty.parameters,
          TEST_DATA__projectWithDerivedProperty.body,
        ),
      );
    });
    projectionCols = await waitFor(() =>
      renderResult.getByTestId(QUERY_BUILDER_TEST_ID.QUERY_BUILDER_PROJECTION),
    );
    expect(
      await waitFor(() =>
        projectionCols.querySelector(`input[value="Full Name With Title"]`),
      ),
    ).not.toBeNull();
    await waitFor(() => getByText(projectionCols, 'Name With Title'));
    expect(
      queryBuilderState.fetchStructureState.projectionState.columns.length,
    ).toBe(1);
    fireEvent.click(
      getByTitle(projectionCols, 'Set Derived Property Argument(s)...'),
    );
    const dpModal = await waitFor(() => renderResult.getByRole('dialog'));
    await waitFor(() => getByText(dpModal, 'Derived Property'));
    await waitFor(() => getByText(dpModal, 'Name With Title'));
    await waitFor(() => getByText(dpModal, 'title'));
    expect(
      await waitFor(() => dpModal.querySelector(`input[value="Mr."]`)),
    ).not.toBeNull();
  },
);