@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
yarnUsing production DB
Once you set .env file you can run graphql server with:
yarn gql:dbYou 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:loadwhich 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:storewhich 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 devIntegration 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_valueYou 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:libin Logmole repo - run
yarn linkin Logmole repo - run
yarn link @kiwicom/logmolein BLKN FE repo infrontendfolder - make sure you use correct
LOGMOLE_GRAPHQL_HOSTvalue in BLKN.envfile (make sure to run Logmole BE locally if you don't want to use deployed Logmole BE) - start BLKN FE locally (run
yarn devinfrontendfolder
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 testyou can also specify path to given test file(s) if you want to run them separately:
yarn test path/to/file.spec.jsWhile developing it may be also handy to run tests in watch mode which re-run related tests whenever code base is changed:
yarn test --watchUnit 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-testattribute (not defaultdata-testid) since it is also used by orbit components. - we use
renderWithThemeutil to providethemecontext to rendered components. - jest dom is applied to use custom matchers on jest's
expectwhich 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
6 years ago
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago