@kiwicom/logmole v0.3.4
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 infrontend
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
infrontend
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:
- Create test covering as small as possible part of feature you working on.
- Run the test and see it fails.
- Develop the part of feature you covered with the test.
- Run the test to confirm your code works.
- 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 defaultdata-testid
) since it is also used by orbit components. - we use
renderWithTheme
util to providetheme
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
4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
6 years ago
6 years ago