Category Archives: Uncategorized

Longest Common Prefix

Task

 

Write a function to find the longest common prefix string amongst an array of strings.

If there is no common prefix, return an empty string "".

 

Example 1:

Input:

 strs = ["flower","flow","flight"]

Output:

 "fl"

Example 2:

Input:

 strs = ["dog","racecar","car"]

Output:

 ""

Explanation:

 There is no common prefix among the input strings.

 

Constraints:

  • 1 <= strs.length <= 200
  • 0 <= strs[i].length <= 200
  • strs[i] consists of only lower-case English letters.

This problem was taken from Leetcode Longest Common Prefix

 

Solution

 

Let’s examine this example:

 strs = ["flower","flow","flight"]

the longest common substring is:

 "fl"

Solution 1: Horizontal scanning

We could assume that the whole word could be the common one so we set prefix = ‘flower’
Then we would compare with the rest words and keep removing last character until prefix becomes empty (meaning no common substring was found) or until we have the common substring.

prefix flower flow flight
flower flower flower flower
flowe flowe flowe flowe
flow flow flow flow
flo flo flo flo
fl fl fl fl

 

/**
 * @param {string[]} strs
 * @return {string}
 */
var longestCommonPrefix = function(strs) {
    var prefix = strs[0];
    for(var i = 1; i < strs.length; i ++ ) {
        while(strs[i].indexOf(prefix) != 0) {
            prefix = prefix.substring(0, prefix.length - 1);
            if(prefix == "")
                return '';
        }
    }
    return prefix;    
};

 what we just did:
– set up prefix to be the whole 1st word strs[0]
– compare prefix with the second word (strs[1]) and if there is no match, remove the last symbol and keep going until it finds match.

Complexity Analysis

  • Time complexity : O(S) , where S is the sum of all characters in all strings.In the worst case all n strings are the same. The algorithm compares the string S1 with the other strings [S_2 \ldots S_n] There are S character comparisons, where S is the sum of all characters in the input array.
  • Space complexity : O(1). We only used constant extra space.

 

Solution 2: Vertical scanning

Similar but optimized for cases like the one above where we have very short common substring, and we don’t want to scan the whole word.

 

prefix flower flow flight
f f f f
fl fl fl fl
flo flo flo flo

 

/**
 * @param {string[]} strs
 * @return {string}
 */
var longestCommonPrefix = function(strs) {
    var prefix;    
    for(var i = 0; i < strs[0].length; i ++ ) {
        var c = strs[0][i];
        for(var j = 0; j < strs.length; j++) {
            if(strs[j][i] != c) {
                return strs[0].substring(0, i);
            }
        }
    }
    return strs[0];    
};

 what we just did:
– Iterate through the words like they are in column.
– compare each character (starting with the first one) between all words. When we find a mismatch, remove the last (mismatched) character and return truncates strs[0]

Trapping Rain Water

Task

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.


image was borrowed from leetcode

The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Thanks Marcos for contributing this image!

Example:

Input:

 [0,1,0,2,1,0,1,3,2,1,2,1]

Output:

 6

This problem was taken from Leetcode

Solution

 

The brute force approach: for each element we go to the right and find the maximum height of the bar, then we go to the left and do the same.

For any element the maximum amount of the water that could be trapped will be the minimum of left height and right height, minus the height of the bar.

So for the array [0,1,0,2,1,0,1,3,2,1,2,1] we go all the way to the right and calculate the max right value, starting from first element ‘0’ max right will be 0. ‘1’ – max right is ‘1’ and so on.
We repeat the same from last element ‘1’ to the first one.

Then the trapped water for the first column will be:  min(maxRight, maxLeft) – theArrayElement[n]

the array 0 1 0 2 1 0 1 3 2 1 2 1
max right 0 1 1 2 2 2 2 3 3 3 3 3
max left 3 3 3 3 3 3 3 3 2 2 2 1
collected
water
0 0 1 0 1 2 1 0 0 1 0 0

 

The complexity will be O(n2)

/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function(height) {
    if(height.length < 2)
        return 0;

    let findMaxLeft = function(idx, height) {
        let max = 0;
        for(let i =idx;i >= 0; i --) {
            max = Math.max(max, height[i]);
        }
        return max;
    }

    let findMaxRight = function(idx, height) {
        let max = 0;
        for(let i = idx;i < height.length; i ++) {
            max = Math.max(max, height[i]);
        }
        return max;
    }  

    let collectedWater = 0;
    for(let i = 0;i < height.length; i ++) {

        const maxLeft = findMaxLeft(i, height);
        const maxRight = findMaxRight(i, height);

        let min = Math.min(maxLeft, maxRight);
        collectedWater += (min - height[i]);
    }

    return collectedWater;
};

The better solution: find all max left and max right with one loop, then do a second loop for each element in the array, and calculate trapped water.

/**
 * @param {number[]} height
 * @return {number}
 */
var trap = function(height) {
    let maxLeftArray = [], maxRightArray = [];
    let maxLeft = 0, maxRight = 0;
    const ln = height.length;
    let trappedWater = 0;

    for(let i = 0;i < height.length; i ++) {
        maxLeftArray[i] = Math.max(height[i], maxLeft);
        maxLeft = maxLeftArray[i];

        maxRightArray[ln - i - 1] = Math.max(height[ln - i - 1], maxRight);
        maxRight = maxRightArray[ln - i - 1];
    }

    for(let i = 0;i < height.length; i ++) {
        trappedWater += Math.min(maxLeftArray[i], maxRightArray[i]) - height[i];
    }
    return trappedWater;

};
what we just did:

– With one loop find the max left and right bar on each side.
– for any element the maximum amount of the water that could be trapped will be the minimum of left height and right height, minus the height of the bar.

Array VS Hash Table

Hash table tutorial

Find element in Array

function fuindInArray() {
  var t0 = performance.now();

  for(var q = 0; q < data2.length; q++) {
      if( data2[q] == '106112407') {
          console.log(data2["106112407"]);
          break;
      }
  }

Find element in Hash Table

function findInHashtable() {
  var t0 = performance.now();

  console.log(data1["106112407"]);

  var t1 = performance.now();
  document.querySelector('#result1').value = "Call took " + (t1 - t0) + " milliseconds.";
}

 


Sort an array

Task

Given an array of integers nums, sort the array in ascending order.

Example 1:

Input:

 [5,2,3,1]

Output:

 [1,2,3,5]

Example 2:

Input:

[5,1,1,2,0,0]

Output:

[0,0,1,1,2,5]

 

This problem was taken from Leetcode

Solution

The brute force solution.

The brute force solution could be to make two loops and iterate through each elements in the array and repeatedly swapping the adjacent elements if they are in wrong order. This approach is called Bubble sort.

var nums = [5,4,6,1,2,3];


/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function(nums) {

  for(var i = 0; i < nums.length - 1;i++) {
    for(var j=0;j < nums.length - 1; j++) {
      if(nums[j] > nums[j+1]) {
        var tmp = nums[j];
        nums[j] = nums[j + 1];
        nums[j + 1] = tmp;
      }
    }
  }
  return nums;
}

nums = sortArray(nums);
console.log(nums);

In the worst case scenario the complexity will be O(n*n) since we have to iterate n*n times where n is the number of elements in the array.

A better performing solution is to keep splitting the array into two halves till we have single elements. Then we start merging the arrays and sorting at the same time. This is called merge sort algorithm.

The diagram is borrowed from Wikipedia.

 

Merge sort algorithm implementation:

/**
 * @param {number[]} nums
 * @return {number[]}
 */
var sortArray = function(nums) {
  function mergeArray(nums, start, mid, end) {
    var i = start, j = mid + 1;
    var tempArr = [];
    // compare till we reach either end of one of the halves
    for(var k = start;(i < mid+1 && j < end+1); k++) {
        if(nums[i] <= nums[j]) {
          tempArr.push(nums[i]);
          i++;           
        }
        else {
          tempArr.push(nums[j]);
          j ++;            
        }       
    }    
    
    // add the rest from the first half 
    for(var k = j;k < end + 1; k++) {
      tempArr.push(nums[k]);
    }         
    // add the rest from the second half 
    for(var k = i;k < mid + 1; k++) {
      tempArr.push(nums[k]);
    }         

    // set up nums with sorted values
    for(var k = start;k < end+1; k++) {
      nums[k] = tempArr[k - start];      
    }
  }

  function mergeSort(nums, start, end) {
    var mid = Math.floor((start + end) / 2);
    if(start < end) {
        mergeSort(nums, start, mid);
        mergeSort(nums, mid + 1, end);
        mergeArray(nums, start, mid, end);
    }
  }
  mergeSort(nums, 0, nums.length - 1);
  return nums;
}
var nums = [5,4,6,1,2,3];
var result = sortArray(nums);
console.log(result);

What we just did?
– started to split the array by half (line 39,40) which recursively calls mergeSort and keep splitting on smaller and smaller pieces till the array has only 1 element (line 37)

mergeSortcall sequence:

Caller Start End
Initial Call 0 5
L 0 2
L 0 1
L 0 0
R 1 1
R 2 2
R 3 5
L 3 4
L 3 3
R 4 4
R 5 5

Adding multiple brands and applications

branch-name:  
Click To Copy

 

Let’s make the task more challenging and assume that we are going to have two different Brands (or apps)

one.localhost.com,
two.localhost.com,

for the most of the cases they will do the same thing but the styling will be different, so let’s add ability for our components to be styled in different ways, depending of the brand.

Passing the sub-domain to the PageLayout component

Let’s add new parameter to store the default brand.

./.env

APP_NAME=Webpack React Tutorial
GRAPHQL_URL=http://localhost:4001/graphql
PROD_SERVER_PORT=3006
DEFAULT_BRAND=one

and make it available to the front end.

./getEnvironmentConstants.js

...
const frontendConstants = [
  'APP_NAME',
  'GRAPHQL_URL',
  'PROD_SERVER_PORT',
  'DEFAULT_BRAND'
];
...

Now, let’s pass the sub domain to the PageComponent. We have to do this in two different places: one for the client side, and one on the SSR.

./components/App/index.js

import React from 'react';
import PageLayout from '../../containers/PageLayout';
import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
import { Provider } from 'react-redux';
import { createStore} from 'redux';
import reducers from '../../reducers';

const styles = require('./styles.scss');

const store = createStore(reducers, {});

export default () => {
  const GRAPHQL_URL = process.env.GRAPHQL_URL;
  const client = new ApolloClient({
    link: new HttpLink({ uri:  GRAPHQL_URL }),
    cache: new InMemoryCache().restore(window.__APOLLO_STATE__),
  }); 
  
  const subDomain = window.location.host.split('.').length == 1 ? process.env.DEFAULT_BRAND : window.location.host.split('.')[0];
  return (
    <div className={styles.appWrapper}>
      <Provider store={store}>
        <ApolloProvider client={client}>
          <Router>
            <Switch>
            <Route exact path="*" render={(props) => <PageLayout {...props} subDomain={subDomain} />} />
            </Switch>
          </Router>
        </ApolloProvider>
      </Provider>
    </div>        
  );
}

 what we just did:
– (line 23) getting the sub domain
– (line 30) passing extra parameter with the subDomain to the PageComponent

Do the same for the SSR

./components/App/ssr-index.js

import React from 'react';
import PageLayout from '../../containers/PageLayout';
import { StaticRouter,  Route, Switch } from 'react-router-dom';
import { ApolloProvider } from 'react-apollo';
import { Provider } from 'react-redux';
import { createStore} from 'redux';
import reducers from '../../reducers';
import fetch from 'isomorphic-fetch';

import styles from './styles.scss';

const store = createStore(reducers, {});

export default ( {req, client} ) => {
  const subDomain = req.headers.host.split('.').length == 1 ? process.env.DEFAULT_BRAND : req.headers.host.split('.')[0];
  const context = {};
  return (
    <div className={styles.appWrapper}>
      <Provider store={store}>   
        <ApolloProvider client={client}>            
          <StaticRouter location={ req.url } context={context}>
            <Switch>
              <Route exact path="*" render={(props) => <PageLayout {...props} subDomain={subDomain} />} />  
            </Switch>            
          </StaticRouter>
        </ApolloProvider>
      </Provider>
    </div>
  );
}

 what we just did:
– (line 23) getting the sub domain, this time from the express request object, since this is happening on the server side.
– (line 30) passing extra parameter with the subDomain to the PageComponent

 

Now this will work fine for the production build, but if you run yarn start-api it will break with  Invalid Host header message.  This is a security feature and we need to tell Webpack dev server that we are going to have sub-domains.
This could be done by passing disableHostCheck: true and since we are using WebpackDevServer only for development this should be safe enough.

./server-api.js

import WebpackDevServer from 'webpack-dev-server';
import webpack from 'webpack';
import config from './webpack.api.config.js';
require('dotenv').config();

console.log(">>>" + process.env.GRAPHQL_URL);

const compiler = webpack(config);
const server = new WebpackDevServer(compiler, {
  hot: true,
  publicPath: config.output.publicPath,
  historyApiFallback: true,
  disableHostCheck: true,
});
server.listen(8080, 'localhost', function() {});

For the CLI config, if will look like this: --disable-host-check

./package.json

...
    "start-cli": "webpack-dev-server --disable-host-check --hot --history-api-fallback --config webpack.cli.config.js",
...

 

 

Passing brand name to the components

This could be achieved in many different ways:
– we could either use redux and add the brand property there, exposing it to all connected components (which we will do later in this tutorial)
– or we could simply pass it as a property from the PageLayout component.

Let’s do the second one since it is straight forward.

./containers/PageLayout/index.js

import React, { Component } from 'react';
import ComponentList from './ComponentList';
import Loading from '../../components/Loading';
import query from './query';
import { graphql } from 'react-apollo';
const styles = require('./styles.scss');
class PageLayout extends Component {
    constructor(props) {
      super(props);      
    } 
  
    render() {
      if(!this.props.data.getPageByUrl) {
        return (<Loading />);
      }     

      const subDomain = this.props.subDomain;

      const allLayout = this.props.data.getPageByUrl.layout.map((layoutList) => {
        const layout = layoutList.components.map((component, id , components) => {
          const componentName = component.name;        
          const ChildComponent = ComponentList[componentName];
          if(typeof ChildComponent === 'undefined') {
            return(
              <div key='{id}' className={styles.error}>Can't find {componentName} component!</div>
            );
          }
          return (
              <ChildComponent key={componentName} subDomain={subDomain}  />
          );
        });
        return layout;
      });
      return(
        <div className={styles.app}>
          {allLayout}
        </div>
      );
    }
}
export default graphql(query, {
    options(props) {
      return {
        variables: {
          url: props.history.location.pathname
        },
      };
    },
  })(PageLayout);

 

Adding brand specific styling to the component

Coming up with a good naming convention and folder structure is very important, but I will leave this to you to research.

I would propose the simplest one:

component
|- brands
|  |_ one
|     |- styles.scss
|  |_ two
|     |- styles.scss
|- index.js

./components/Home/index.js

import React from 'react';

const Home = ( {subDomain} ) => {
  const styles = require(`./brands/${subDomain}/styles.scss`);

  return (
    <div>
      <div className={styles.wrapper}>This is my home section!</div>
    </div>
  )
}
export default Home;

yarn start

hit one.localhost:3006/home and then two.localhost:3006/home and you will notice the difference right away!

But how this was achieved without re-rendering the component?

Let’s look at component’s css file (it might be 1.css or different number but look at the net tab and make sure that the CSS contains something like this):

.one-wrapper{background:#8d8dac;color:#fff}.one-wrapper,.two-wrapper{text-align:center;font-family:MyFont}.two-wrapper{background:#fff;color:#000}

– first brand has .one-wrapper{background:#8d8dac;color:#fff} and the second has two-wrapper{background:#fff;color:#000} and all CSS that is in common is added like this: .one-wrapper,.two-wrapper{text-align:center;font-family:MyFont} so no repeating CSS if it’s the same between brands.
Basically now the css file for this component will have all styling for both brands.
This is nice but not perfect. We still load unnecessarily the styles for the brands that we are not in to. Let’s see how to fix this in the next chapter.

branch-name:  
Click To Copy

 

 

 

Adding html.js component

branch-name:  
Click To Copy

 

Let’s clean up the code a bit and move the html code from ./ssr-server.js to a separate component called html.js.

./html.js

import React from 'react';

const Html = ({ content, cssBundles, jsBundles, apolloClient }) => (
  <html lang="en">
  <head>
    <meta charSet="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Server Side Rendering and Bundle Splitting</title>
    <link
    href="/dist/main.css"
    rel="stylesheet"
    as="style"
    media="screen, projection"
    type="text/css"
    charSet="UTF-8"
  />

    {
      cssBundles.map( (bundle) => 
        (<link
          href={`${bundle.publicPath}`}
          rel="stylesheet"
          as="style"
          media="screen, projection"
          type="text/css"
          charSet="UTF-8"
        />))
    }

    {jsBundles.map( ( {file}) => (<script src={`/dist/${file}`}>{file}</script>) )}
  </head>
  <body cz-shortcut-listen="true">
    <div id="root" dangerouslySetInnerHTML={{ __html: content }} />
    <script dangerouslySetInnerHTML={{
          __html: `window.__APOLLO_STATE__=${JSON.stringify(apolloClient.cache.extract())}`}} />
  
    <script src="/dist/main-bundle.js"></script>
  </body>
</html>  

);

export default Html;

what we just did:
– we created a plain react component that will accept 3 parameters:
    – content [string]: rendered components returned from react-apollo  renderToStringWithData(…)
    – cssBundles [array of CSS bundle objects]:
    – jsBundles [array of js bundle objects]:
apolloClient [Object]:
An instance of the Apollo client.

 

And now, let’s remove the HTML coder from ./server.js and add the new component instead, passing all necessary parameters.

./server-js

import React from 'react';
import express from 'express';
import App from './src/components/App/ssr-index';
import Loadable from 'react-loadable';
import manifest from './dist/loadable-manifest.json';
import { getDataFromTree } from "react-apollo";
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { renderToStringWithData } from "react-apollo"
import { createHttpLink } from 'apollo-link-http';
import { getBundles } from 'react-loadable/webpack';
import ReactDOMServer from 'react-dom/server';
import Html from './html.js';

const PORT = process.env.PROD_SERVER_PORT;
const app = express();

app.use('/server-build', express.static('./server-build'));
app.use('/dist', express.static('dist')); // to serve frontent prod static files
app.use('/favicon.ico', express.static('./src/images/favicon.ico'));

app.get('/*', (req, res) => {

  const GRAPHQL_URL = process.env.GRAPHQL_URL;
  const client = new ApolloClient({
    ssrMode: true,
    link: createHttpLink({
     uri: GRAPHQL_URL,
     fetch: fetch,
     credentials: 'same-origin',
     headers: {
       cookie: req.header('Cookie'),
     },
   }), 
    cache: new InMemoryCache()
  });    

  // Prepare to get list of all modules that have to be loaded for this route
  const modules = [];
  const mainApp = (
    <Loadable.Capture report={moduleName => modules.push(moduleName)}>
      <App req={req} client={client} />
    </Loadable.Capture>    
  );

  // Execute all queries and fetch the results before continue
  getDataFromTree(mainApp).then(() => {        
    // Once we have the data back, this will render the components with the appropriate GraphQL data.
    renderToStringWithData(<App req={req} client={client} />).then( (HTML_content) => {
      // Extract CSS and JS bundles
      const bundles = getBundles(manifest, modules); 
      const cssBundles = bundles.filter(bundle => bundle && bundle.file.split('.').pop() === 'css');
      const jsBundles = bundles.filter(bundle => bundle && bundle.file.split('.').pop() === 'js');
    
      const html = <Html content={HTML_content} cssBundles={cssBundles} jsBundles={jsBundles} apolloClient={client} />;

      res.status(200);
      res.send(`<!doctype html>\n${ReactDOMServer.renderToStaticMarkup(html)}`);
      res.end(); 
    });    



  }).catch( (error) => {
    console.log("ERROR !!!!", error);
  });
});

Loadable.preloadAll().then(() => {
  app.listen(PORT, () => {
    console.log(`😎 Server is listening on port ${PORT}`);
  });
});
branch-name:  
Click To Copy

 

 

Sorting algorithms

Task

Sort an array of integers in descending order.

Example:

Given an array of 4 elements

 [3,15,1,5]

Produce an array that will look like this:

 [1,3,5,15]

 

Solution

Brute force using the “selection sort” algorithm.

  1. Starting with the first number 3 we assume that this is our smallest number since we don’t know the others.
  2. Then we iterate through the whole array comparing 3 with other numbers.
  3. If we find number that is smaller than 3 (in our case 1) this will become our smallest number, and we continue comparing it to the end of the array.
  4. when we reach the end of the array we swap the first number, that we assumed that is the smallest one with the newly discovered smallest number (in our case number 1 at third position), and the array will look like this now: [1,15,3,5]
  5. Now as we know for sure that the number at the first position (number 1) is the smallest one, we can start from position 2 and repeat steps 2 to 5 till we checked all numbers in the array.

 

function sortArray(list) {
    var smallestIndex;    
    for(var i = 0; i < list.length; i ++) {
        smallestIndex = i;
        for(var ii = i + 1; ii < list.length;ii ++) {
            smallestIndex = list[ii] < list[smallestIndex] ? ii : smallestIndex;
        }
        var larger = list[i];
        list[i] = list[smallestIndex];
        list[smallestIndex] = larger;

    }
    return list;
}

var listArray = [3,15,1,5];
var result = sortArray(listArray);

console.log(result);

 

Better approach:

Merge sort algorithm.

Let’s consider this example with adding more numbers: [3,15,1,5,4,9]

3,15
1,5
--------
9,4


1,3,5,15	| 1
4,9			

3,5,15		| 1,3
4,9			

5,15		
4,9		| 1,3,4

5,15
9		| 1,3,4,5

15
9		| 1,3,4,5,9

15		| 1,3,4,5,9,15

coming soon…

Adding Jest and Enzyme and creating snapshot tests

branch-name:  
Click To Copy

 

In this tutorial we will learn how to install Jest and Enzyme, and create Snaphot tests.

If you are not familiar with these tools please read the links first since we will focus mainly on installing and using the tools.

The basic idea is that we are going to create a test cases, that will render the component, and create a snapshot of it. Next time when we run the test we could compare with the snapshot, making sure that either the component is not changed, or it renders as expected.

Setting up Jest and Enzyme.

We will need some extra packages in order to make Jest work with Enzyme.

Installing the packages.

  • enzyme – the actual enzyme testing utility.
  • enzyme-adapter-react-16 – the react adapter for enzyme. There are different adapters for different React versions, and since we are using react 16 we will install this version. A full list of adapter packages could be found here
  • enzyme-to-json – a serializer for the components.
yarn add jest enzyme enzyme-adapter-react-16 enzyme-to-json --dev

In addition we have to also install:

yarn add babel-jest --dev

  • babel-jest – allows Jest to use babel to transpile JS.

Before using Enzyme we have to configure it first. Create file with the following contents:

src/setupTests.js

import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });

This tells Enzyme, to run with React using the adaptor we just installed. Make sure this file is included before using Enzyme. Open package.json and add the following config:

./package.json

...
  },
  "jest": {
    "setupFiles": [
      "./src/setupTests.js"
    ]
  },
...

Now we are almost ready to write our first test. Header component is perfect candidate since it is not connected to the redux store.

Create new file in the ./src/components/Header folder and let’s

Add a Snapshot Test

./src/components/Header/index.test.js

import React from 'react';
import { shallow } from 'enzyme';
import Header from './index';
import toJson from 'enzyme-to-json';

describe('Testing Header component', () => {
    it('renders as expected!!!', () => {
      const wrapper = shallow(
        <Header title="Title test" />
      );
     expect(toJson(wrapper)).toMatchSnapshot();
   });
});

 unfortunately if we do yarn test it will fail with error code, since Jest doesn’t know how to deal with anything different than Java Script.
But there is an easy fix: we could map CSS to a JS file that Jest understands. Create empty file in

./src/__mocks__/styleMock.js


and set up Jest to use the mapping:

./package.json

...
  "jest": {
    "setupFiles": [
      "./src/setupTests.js"
    ],
    "moduleNameMapper": {
      "\\.(css|less|scss)$": "<rootDir>/src/__mocks__/styleMock.js"
    }    
  },
...

Now let’s run yarn test and we should see something like this:

yarn test
yarn run v1.12.3
$ jest
 PASS  src/components/Header/index.test.js
  Testing Home component
    ✓ renders as expected!!! (15ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   1 passed, 1 total
Time:        2.194s
Ran all test suites.
✨  Done in 3.05s.

Perfect ! Our first snapshot test is done.

Testing renderer component

src/components/Home/renderer.test.js

import React from 'react';
import { shallow, mount } from 'enzyme';
import toJson from 'enzyme-to-json';
import Component from './renderer';

describe('Testing Home component', () => {
    it('renders as expected!!!', () => {
      const wrapper = shallow(<Component styles={{}} title="MyTest" />);

     expect(toJson(wrapper)).toMatchSnapshot();
     expect(wrapper.contains(<div>MyTest</div>)).toBe(true);
   });
});

Also we added expect(wrapper.contains(<div>MyTest</div>)).toBe(true); which looks for certain things into the component.

Testing Redux connected component.

./src/components/About/index.test.js

import React from 'react';
import { mount } from 'enzyme';
import toJson from 'enzyme-to-json';
import { Provider } from 'react-redux';
import reducers from '../../reducers';
import { createStore} from 'redux';
import About from './index.js';

const store = createStore(reducers, {});
let wrapper;
describe('Testing About component', () => {

  beforeEach(() => {
    // Runs a function before each of the tests in this file runs
    wrapper = mount(
      <Provider store={store}>
        <About userName="Toni" />
      </Provider>
    );
  });

  it('renders as expected', () => {
   expect(toJson(wrapper)).toMatchSnapshot();
  });

  it('expect to have state.userName set to John', () => {   
   wrapper.setState({userName: "John"});
   expect(wrapper.state('userName')).toEqual("John");
  });  
});

since we wrapped MyComponent with Provider component, we have to mount the component that will render it in full instead of doing shallow rendering (like in the previous example). Otherwise shallow will render only the Provider component.

Now the two tests could be executed together yarn test

 PASS  src/components/About/index.test.js
  Testing About component
    ✓ renders as expected (56ms)
    ✓ expect to have state.userName set to John (14ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   1 passed, 1 total
Time:        3.102s
Ran all test suites matching /.\/node_modules\/jest\/bin\/jest.js|src\/components\/About\/index.test.js/i.

Adding more tests.

./src/components/Greetings/renderer.test.js

import React from 'react';
import { mount } from 'enzyme';
import toJson from 'enzyme-to-json';
import reducers from '../../reducers';
import { createStore} from 'redux';
import Component from './renderer';

const store = createStore(reducers, {});
let wrapper;
describe('Testing Greetings component', () => {

  beforeEach(() => {
    wrapper = mount(
        <Component styles={{}} store={store} />
    );
  });

  it('renders as expected', () => {
    // to snapshot test
    expect(toJson(wrapper)).toMatchSnapshot();
  });

  it('textbox does not exist', () => {  
    expect(wrapper.find('input').exists()).toBe(false);
  });

  it('textbox exists after h2 click', () => {  
    // simulate click on the h2 tag trigering props change and visualizing the input text box
    wrapper.find('h2').simulate('click');
    expect(wrapper.find('input').exists()).toBe(true);
  });    

  it('textbox values change properly', () => {    
    // text box value tests
    expect(wrapper.find('input').props().value).toBe('No Name');
    wrapper.find('input').props().value = 'test';
    expect(wrapper.find('input').props().value).toBe('test');
  });    

});
  • beforeEach – Runs a function before each of the tests in this file runs. If the function returns a promise or is a generator, Jest waits for that promise to resolve before running the test.
  • here is another way of passing the store directly to the component.
branch-name:  
Click To Copy

Adding Middleware to log requested IPs

Middleware is a good way to pre-process the request before handing it out to GraphQL.
We could add authentication that could check the auth token in the header or middleware that will log the request IPs. Let’s do the second one.

Adding middleware could be done the same way as we are doing it in Express app. Add the highlighted lines, and let’s see what they do.

var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');
var dogs = require('./src/models/mock_data/dogs.js');
// Construct a schema, using GraphQL schema language
var schema = buildSchema(`
  type Mutation {
    setNewDog(id: String, breed: String, displayImage: String): String
  }  
  type dog {
    id: String,
    breed: String,
    displayImage: String
  }
  type dogsCatalog {
    getDogByBreed(breed: String): dog
  }
  type Query {
    queryDogs: dogsCatalog
  }  
`);
class dogsCatalog {
  
  getDogByBreed({breed}) {
    var result = dogs.find(function(dog){
      return breed == dog.breed ? dog : null;
    });
    return result;
  }
}
// The root provides a resolver function for each API endpoint
var root = {
  queryDogs: () => {
    return new dogsCatalog();
  },
  setNewDog: ({id, breed, displayImage}) => {
    var result = dogs.find(function(dog){
      return breed == dog.breed ? dog : null;
    });
    if(result != null) {
      return 'dog already exists!';
    }
    dogs.push({
      "breed": breed,
      "displayImage": displayImage,
      "id": id      
    });
    return 'dog added successfully !';
  }
};

// Logger middleware
var logger = function(req, res, next) {
  console.log("GOT REQUEST >", req.ip);
  next(); // Passing the request to the next handler in the stack.
}

var app = express();
app.use(logger);
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));
app.listen(4001);
console.log('Running a GraphQL API server at localhost:4000/graphql');

 

 what we just did:
– we created the middleware object to log every request ip (lines 52-56)
– in the middleware we added the next() function to proceed to the next middleware none we are done with the request processing.
– we told Express to use our new middleware app.use(logger);

And as simple as that we created our own logging class.

Mutations and input types

 

Let’s scrap our user greeting and make really useful schema, that will show up a dog breed catalog. We are going to use it in another tutorial to create a React component to display the dog catalog.

Set up mock data.

Create a file under ./src/modules/mock_data that will serve as our data provider. Let’s add a couple of dog breeds there.

./src/modules/mock_data/dogs.js

module.exports = [
  {
    "breed": "affenpinscher",
    "displayImage": "https://images.dog.ceo/breeds/affenpinscher/n02110627_10225.jpg",
    "id": "Z1fdFgU"
  },
  {
    "breed": "african",
    "displayImage": "https://images.dog.ceo/breeds/african/n02116738_2515.jpg",
    "id": "Z1gPiBt"
  },
  {
    "breed": "airedale",
    "displayImage": "https://images.dog.ceo/breeds/airedale/n02096051_6335.jpg",
    "id": "ZNDtCU"
  },
  {
    "breed": "akita",
    "displayImage": "https://images.dog.ceo/breeds/akita/Japaneseakita.jpg",
    "id": "6HalQ"
  },
  {
    "breed": "appenzeller",
    "displayImage": "https://images.dog.ceo/breeds/appenzeller/n02107908_3311.jpg",
    "id": "Z1tlucN"
  },
  {
    "breed": "basenji",
    "displayImage": "https://images.dog.ceo/breeds/basenji/n02110806_3415.jpg",
    "id": "Zo1k5y"
  }  
];

Writing a query to fetch the mock data.

Let’s first write a query to fetch dog’s details based on the breed.

Based on the JSON data above, dog’s schema would look like this:

...
  type dog {
    id: String,
    breed: String,
    displayImage: String
  }
  type dogsCatalog {
    getDogByBreed(breed: String): dog
  }
  type Query {
    queryDogs: dogsCatalog
  } 
...

We created a dogCatalog query (line 7) that accepts the dog breed, and returns a dog type declared in the beginning of the sniped above.

Then the resolver would look like this:

...
class dogsCatalog {
  
  getDogByBreed({breed}) {
    var result = dogs.find(function(dog){
      return breed == dog.breed ? dog : null;
    });
    return result;
  }
}
...

 

Write the mutation.

The mutations are used to modify data. In GraphQL they are similar as queries and could be written in a similar way.

The basic idea.

...
  type Mutation {
    setNewDog(id: String, breed: String, displayImage: String): String
  }  
...

here, we defined mutation named setNewDog which simply takes a few parameters (id, breed and displayImage) and returns the status message.

The resolver will look like this:

...
  setNewDog: ({id, breed, displayImage}) => {
    var result = dogs.find(function(dog){
      return breed == dog.breed ? dog : null;
    });
    if(result != null) {
      return 'dog allready exists!';
    }

    dogs.push({
      "breed": breed,
      "displayImage": displayImage,
      "id": id      
    });
    return 'dog added successfuly !';
  }
...

Let’s put them all together:

./src/server.js

var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');
var dogs = require('./src/models/mock_data/dogs.js');

// Construct a schema, using GraphQL schema language

var schema = buildSchema(`


  type Mutation {
    setNewDog(id: String, breed: String, displayImage: String): String
  }  

  type dog {
    id: String,
    breed: String,
    displayImage: String
  }

  type dogsCatalog {
    getDogByBreed(breed: String): dog
  }

  type Query {
    queryDogs: dogsCatalog
  }  
`);

class dogsCatalog {
  
  getDogByBreed({breed}) {
    var result = dogs.find(function(dog){
      return breed == dog.breed ? dog : null;
    });
    return result;
  }
}

// The root provides a resolver function for each API endpoint
var root = {
  queryDogs: () => {
    return new dogsCatalog();
  },
  setNewDog: ({id, breed, displayImage}) => {
    var result = dogs.find(function(dog){
      return breed == dog.breed ? dog : null;
    });
    if(result != null) {
      return 'dog already exists!';
    }

    dogs.push({
      "breed": breed,
      "displayImage": displayImage,
      "id": id      
    });
    return 'dog added successfully !';
  }
};

var app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');

Test Using GraphQL UI

Writing a query to fetch existing data.

Now navigate to GraphQL UI http://localhost:4001/graphql and add this in the query window (up left)

query queryLabel($breed: String!) {
  queryDogs {
    getDogByBreed(breed: $breed) {
      id
      displayImage,
      breed
    }
  }
}

and this in the variables window (down left)

{
  "breed": "african"
}

hit play and the result should be this:

{
  "data": {
    "queryDogs": {
      "getDogByBreed": {
        "id": "Z1gPiBt",
        "displayImage": "https://images.dog.ceo/breeds/african/n02116738_2515.jpg",
        "breed": "african"
      }
    }
  }
}

Now change the breed in the variables window (down left) to ‘labrador’ and hit play again, and the result is null since there is no record for this breed.

Write a mutation to add a new record to data structure.

Open a new QraphQL UI tab and add the following mutation in the query window:

mutation setNewDogLabel($id: String, $breed: String, $displayImage: String) {
  setNewDog(id: $id, breed: $breed, displayImage: $displayImage)
}

and this in the variables window:

{
  "id": "Z2iEkxg",
  "breed": "labrador",
  "displayImage": "https://images.dog.ceo/breeds/labrador/n02099712_2120.jpg"
}

Hit play and if everything went well you will see the success message.

{
  "data": {
    "setNewDog": "dog added successfuly !"
  }
}

(Out of curiosity you might want to hit play one more time to see what message you will get)

Now, navigate back to the previous GraphQL UI tab and hit play again and you will see newly created record.

 

 

 

 

var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');
var dogs = require('./src/models/mock_data/dogs.js');

// Construct a schema, using GraphQL schema language

var schema = buildSchema(`

  input DogInput {
    id: String,
    breed: String,
    displayImage: String
  }

  type Mutation {
    setNewDog(input: DogInput): String
  }  

  type dog {
    id: String,
    breed: String,
    displayImage: String
  }

  type dogsCatalog {
    getDogByBreed(breed: String): dog
  }

  type Query {
    queryDogs: dogsCatalog
  }  
`);

class dogsCatalog {
  
  getDogByBreed({breed}) {
    var result = dogs.find(function(dog){
      return breed == dog.breed ? dog : null;
    });
    return result;
  }
}

// The root provides a resolver function for each API endpoint
var root = {
  queryDogs: () => {
    return new dogsCatalog();
  },
  setNewDog: ({input}) => {
    var result = dogs.find(function(dog){
      return input.breed == dog.breed ? dog : null;
    });
    if(result != null) {
      return 'dog allready exists!';
    }

    dogs.push({
      "breed": input.breed,
      "displayImage": input.displayImage,
      "id": input.id      
    });
    return 'dog added successfuly !';
  }
};

var app = express();
app.use('/graphql', graphqlHTTP({
  schema: schema,
  rootValue: root,
  graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');