0.3.4 • Published 4 years ago

@kiwicom/logmole v0.3.4

Weekly downloads
20
License
MIT
Repository
-
Last release
4 years ago

Logmole

Kiwi's logs analytical tool

Usage

Add @kiwicom/logmole package into your project using yarn add @kiwicom/logmole and import it as a React component.

Developing guide

To develop Logmole you need to set environmental variables so copy .env and googleCredentials.json from 1Password to project root folder. googleCredentials.json is used to connect with BigQuery project.

Note: VPN connection is necessary.

Installation

To install the project you need to:

#this will install also peerDependencies
yarn

Using production DB

Once you set .env file you can run graphql server with:

yarn gql:db

You can also uncomment LOGMOLE_GRAPHQL_HOST in .env file (and restart node dev server) to connect directly to production BE. To be able to do that you'll need to get valid JWT token from BLKN.

Using DB mocks

You can use DB mocks by running:

yarn gql:load

which will load JSON data from /data/mockData. You can change the file that will be loaded in here, download mock examples from 1Password and store it into /data/mockData folder.

You can also create new mocks by running:

yarn gql:store

which will store received data from DB and store it into /data/mockData using current datetime as a name of a file.

Starting local node dev server

To start node dev server run:

yarn dev

Integration with Sentry

Sentry provides monitoring of all unexpected events which happens in logmole. Before deployment in production environment following .env variables must be set:

LOGMOLE_FE_DSN=https://<FE_key>@sentry.skypicker.com/<FE_project>
LOGMOLE_BE_DSN=https://<BE_key>@sentry.skypicker.com/<BE_project>

Integration with Balkan graphql server

Logomole uses Balkan graphql for fetching of airline/airport data. For development you need to set following variables in .env file:

BALKAN_GRAPHQL_HOST=https://balkan.kiwi.com/graphql
BALKAN_AUTH_TOKEN=token_value

You can get BALKAN_AUTH_TOKEN value after login on https://balkan.kiwi.com/ from Cookies(JWT_Token).

Integration with Balkan platform

Notes

Logmole is imported into BLKN as a single React component using dynamic import to overcome issues with server side rendering in Next.js used in BLKN.

See an example how is it implemented:

const DynamicLogmole = dynamic(() => import('@kiwicom/logmole'), {
  ssr: false
});

const Logmole = props => (
  <Layout noPadding error={props.error} title="Logmole">
    <DynamicLogmole offsetHeight={offsetHeight} />
  </Layout>
);

React dependency is defined as peerDependency because React doesn't like to have multiple instances in node_modules folder. By doing this yarn won't install second dependency in BLKN neither install react package for development purposes. To install react for development we use install-self-peers library.

Process

To test the integration with BLKN you need to:

  • re-fetch BLKN repos to make sure you use the latest master versions
  • start BLKN GQL locally (or use sandbox version)
  • run yarn build:lib in Logmole repo
  • run yarn link in Logmole repo
  • run yarn link @kiwicom/logmole in BLKN FE repo in frontend folder
  • make sure you use correct LOGMOLE_GRAPHQL_HOST value in BLKN .env file (make sure to run Logmole BE locally if you don't want to use deployed Logmole BE)
  • start BLKN FE locally (run yarn dev in frontend folder

After every change in Logmole:

  • run yarn build:lib
  • re-run BLKN FE

Testing

We use jest as a test runing framework. To run all tests:

yarn test

you can also specify path to given test file(s) if you want to run them separately:

yarn test path/to/file.spec.js

While developing it may be also handy to run tests in watch mode which re-run related tests whenever code base is changed:

yarn test --watch

Unit tests are critical in order to obtain clean and scalable code, ideally 100% of code should be test-covered. The purposes of unit testing are:

  • Creating bug-free scallable code
  • Prevent introducing breaking changes while refactoring
  • Bringing confidence to developer experience when working with code

Test files should be placed in path/to/TestedFile/__tests__ and should be named TestedFile.spec.js.

Test Driven Development (TDD)

TDD as the best practice should be always applied when developing logmole. Whenever developing new features try to follow:

  1. Create test covering as small as possible part of feature you working on.
  2. Run the test and see it fails.
  3. Develop the part of feature you covered with the test.
  4. Run the test to confirm your code works.
  5. Repeat

Property based testing

Whenever the test result depends on input of given type - input should be randomised to confirm that code works regardless of input.

For example having a function:

function add(a, b) {
  return a + b;
}

Proper test should contain randomized input:

describe('add', () => {
  it('adds two numbers', () => {
    const a = Math.random();
    const b = Math.random();
    const c = a + b;

    expect(add(a, b)).toBe(c);
  });
});

In more complex cases using fake data generators like faker may be handy.

Ideally single test should be repeated with different set of data using it.each.

it.each([[Math.random(), Math.random()], [Math.random(), Math.random()]])(
  'adds values',
  (a, b) => {
    const c = a + b;
    expect(add(a, b)).toBe(c);
  }
);

Property based testing frameworks as JS Verify are considered to be implemented in the future.

Testing UI components

To encourage the best practices and avoid testing implementation details we use React Testing Library.

Since there are no QA testers working on logmole currently it is very important to simulate manual tester behavior as much as possible. React Testing Library utilizes React-DOM render method to render given component directly. Rendering return set of queries which enable finding affected nodes in the DOM. Therefore jest may "see" the same result as the final user. RTL also comes up with set of method allowing interactions with components.

Keep in mind:

  • for querying by test id we use data-test attribute (not default data-testid) since it is also used by orbit components.
  • we use renderWithTheme util to provide theme context to rendered components.
  • jest dom is applied to use custom matchers on jest's expect which facilitates working with the DOM nodes.

Testing Query Renderers

To test relay QueryRenderer components we use query mock for mocking relay responses. This library provides mock data in asynchronous way allowing to simulate API response to the component.

To be sure that QueryRenderer loads proper mock data you need to force fetching in test environment to avoid getting cached data from prevous test:

<QueryRenderer
  cacheConfig={{ force: process.env.NODE_ENV === 'test' }} /* other props */
/>

example for given QueryRenderer:

const DataContainer = () =>
  <QueryRenderer
    cacheConfig={{ force: process.env.NODE_ENV === 'test' }}
    query={graphql`
       query DataContainerQuery {
           id
         }
      `}
     onLoading={/*...*/}
     onSystemError={/*...*/}
     onResponse={props => <div>{id}</div>}
  />
// DataContainer.spec.js
import { wait } from 'react-testing-library';
import {renderWithTheme, queryMock} from './testUtils';

describe("dataContainer", () => {
  it('renders given data', () => {
    queryMock.mockquery({
      // name must match query name
      name: DataContainerQuery
      data: {id: 'VeryUniqueID'}
    })

    const { getByText } = renderWithTheme(<DataContainer />);

    // await for resolving response
    await wait(() => {
      getByTestId('VeryUniqueID');
    });

    expect(getByText("VeryUniqueID")).toBeInTheDocument();
  })
})

Testing Fragment Containers

Usually composition rendered by QueryRenderer is too complex to cover it in one test file. Therefore UI of fragment containers could be tested in isolation.

To test fragment container it needs to be rendered wrapped in custom QueryRenderer containing testQuery with the proper fragment. Example for given component:

import {createFragmentContainer, graphql} from '@kiwicom/relay'

const DataComponent = ({data}) => <div>{data.id}</data>

export default createFragmentContainer(DataComponent, {
  data: graphql`
     fragment DataComponent_data on RootQuery {
       id
     }
  `
})
// DataComponent.spec.js
import {QueryRenderer} from '@kiwicom/relay'
import {queryMock, renderWithTheme} from './testUtils'

descrbe('DataComponent', () => {
  it('renders id', () => {
     queryMock.mockquery({
      name: DataComponentTestQuery
      data: {id: 'VeryUniqueID'}
    })

   const { getByText } = renderWithTheme(
      <QueryRenderer
        cacheConfig={{force: true}}
        query={graphql`
          query DataComponentTestQuery {
             ...DataComponent_data
           }
       `}
        onLoading={/*...*/}
        onSystemError={/*...*/}
        onResponse=(props => <DataComponent data={props} />
      />
    )

    // await for resolving response
    await wait(() => {
      expect(getByText("VeryUniqueID")).toBeInTheDocument()
    });
  })
})

License

MIT