Tuesday, 23 April 2019

React - Testing with Jest

This post is just a basic of how to setup for Jest testing with React. There will be more posts in the future to cover different assertion types, how to integrate Jest tests into your CI pipeline and how to use Enzyme for rendering components. By default Jest is included in React, and it will pick up tests in either the __tests__ directory or with a filename ending in .test.js. There are different advantages to each of these. I personally feel like __tests__ is better for larger projects, but this may be down to my C# background where you'll commonly have a separate project for tests. As I tend to be of the opinion that you can always grow a program overtime I tend to favour this in all cases. However, the advantage of appending .test.js to the end of your filename is that your file can reside in the same directory as the source code you're testing, which means that import statements tend to be a bit less complicated. Whatever your decision, it's supported and Jest will pick up your test cases.

By default, your React app likely has an App.test.js file which I moved and renamed to __tests__/App.js. Now I was pretty bad and didn't write any test cases for a while, and went on developing without running this test... So, when I finally ran Jest tests using the command
npm test
I found my test was failing! The first cause of failure was that when I rendered my App on index.js I had actually surrounded it in a <BrowserRouter> tag, which meant that when <App> was rendered on its own, it was missing the <BrowserRouter> surrounding the <Switch> and <Route>s. So this test actually allowed me to refactor my code into a more readable and sensible structure... And that was just the basic included test!

But my test was still failing! The error was:
TypeError: window.matchMedia is not a function
The cause of this is that Jest uses JSDom to create a browser environment, which doesn't support window.matchMedia, and I was using this in my code. After some googling, it turns out that it could be mocked out. So I created a __mocks__/matchMedia.js file with the following contents:

  window.matchMedia = jest.fn().mockImplementation(query => {
    return {
      matches: false,
      media: query,
      onchange: null,
      addListener: jest.fn(),
      removeListener: jest.fn(),
    };
  });

In my __tests__/App.js file I imported this mock and low and behold a different error! This error was about having a store, I used the "redux-mock-store" npm package to mock out my redux store and wrapping my <App> in a <Provider>, and my __tests__/App.js file ended up looking like this:

  import React from 'react';
  import ReactDOM from 'react-dom';
  import '../__mocks__/matchMedia';
  import { Provider } from 'react-redux';
  import configureMockStore from 'redux-mock-store';
  import App from '../App';

  const mockStore = configureMockStore();
  const store = mockStore({});

  describe('App', () => {
    it('renders without crashing', () => {
      const div = document.createElement('div');
      ReactDOM.render((
        <Provider store={store}>
          <App />
        </Provider>
      ), div);
    });
  });

And guess what... It PASSED!

Friday, 12 April 2019

React/React Native - Higher Order Components

Higher order components is probably one of my favourite patterns that I have come across for React/React Native. It is when you pass a component to a function and it returns the component with additional functionality. This is very useful for code reuse. You may have seen this used before with the connect function provided by redux. I typically use this for auto login if a user were to navigate to a login/register page e.g.

  import React, { Component } from 'react'
  import { connect } from 'react-redux'
  import { setCurrentUser } from '../actions/index'
  import { bindActionCreators } from 'redux'

  export const withAutoLogin = (Scene) => {
    // New component that wraps our existing component and adds
    // additional functionality
    class WithAutoLogin extends Component {

      constructor(props) {
        super(props);

        // If there is a current user, navigate to home page
        if (this.props.currentUser) {
          ...  
        }
      }

      render() {
        return (
          // apply props to component
          <Scene {...this.props} />
        );
      }
    }

    // Connecting to redux
    function mapStateToProps(state) {
      return {
        currentUser: state.currentUser
      }
    }

    function mapDispatchToProps(dispatch) {
      return bindActionCreators({
        setCurrentUser: setCurrentUser
      }, dispatch);
    }

    return connect(mapStateToProps, 
                   mapDispatchToProps)(WithAutoLogin);
  }

Google Cloud Build - Automating Function Deployment

The following is how to setup a basic google cloud function deployment using google cloud build.
  1. Go to Google Cloud Build (https://console.cloud.google.com/cloud-build/triggers)
  2. Add trigger
  3. Select your source
  4. Authenticate with your source
  5. Adjust your trigger settings
  6. Under Build Configuration, select cloudbuild.yaml
  7. Add substitution variable "_NAME" and provide the name of the function you want to deploy
  8. Add a cloudbuild.yaml to your repository e.g.
    steps:
    - name: 'gcr.io/cloud-builders/gcloud'
      args: ['beta', 'functions', 'deploy', '${_NAME}', 
             '--trigger-http']
    
Note: at some point you should add additional steps for unit tests to ensure that if any unexpected behaviour occurs your build will fail and your function will not be deployed

If you have any permission issues you might need to go to Project Settings > IAM and make sure a member exists with the following roles:
  • Cloud Build Service Account
  • Cloud Build Editor
  • Cloud Functions Developer

Monday, 12 November 2018

Sharing JS Code Across BitBucket Repositories

Recently, I have been writing APIs in node.js with the express framework, a React website and a React Native app that have some shared code. These are spread across several repositories, so I needed a way to share code across the repositories. So I created a node package in another repository.

Then I needed to figure out how to access the repository and install my package via npm. Fortunately, this proved to be relatively easy… Go to your account's BitBucket Settings > App Passwords. From here, you can create an app password and grant read access to your repositories. Then all you need to do is go to your package.json and add to your dependencies e.g.

  {
    …
    dependencies: {
      …,
      "[PACKAGE_NAME]": "git+https://[BITBUCKET_USERNAME]:
               [APP_PASSWORD]@bitbucket.org/PROJECT_NAME/
               REPOSITORY_NAME.git#BRANCH_NAME"
    },
    …
  }


If you then execute the "npm install" command this should install the repo to your node_modules folder.

But what happens when you push changes to the common repository? How do I run the code with the latest commits? Simply add an additional line to your package.json scripts:

  {
    …
    scripts: {
      …,
      "clean-start": "rm -rf -- node_modules/[PACKAGE_NAME]; 
                      npm install; npm start;"
    },
    …
  }


Then when you want to run with the latest changes use
npm run clean-start
instead of
npm start

Friday, 2 November 2018

How to create a VERY basic (and useless) Node Package

This is just the absolute basics of a node package so that you can install it via npm, whether it be a private or publicly accessible repo. See my follow up post about how to install a package that's in a private bitbucket repo.

Firstly, setup a basic package.json as follows:

    {
        "name": "tripwiretech-common",
        "version": "1.0.0",
        "description": "Tripwiretech common code",
        "main": "index.js",
        "author": "Tripwiretech",
        "license": "ISC",
        "scripts": {
            "start": "node index.js"
        }
    }


The name is one of the key properties in the package.json as it will represent your node package name. Any repo with a package.json can be published to npm. It is also recommended that your repo contains a readme.md in the root of the repo, this will be the documentation displayed on the npm website, if you choose to publish it there.

Next you will need some content exported in your index.js that other code can import and use. For the purposes of this example, I've kept it quite simple:

    module.exports = {
        Countries: ["Australia", "New Zealand", "South Africa"],
        Sports: ["Rugby", "Cricket"]
    }


Then you can import it and use it in your code. If you're struggling with more complicated node packages, then I recommend taking a look at one of the many existing and much more practical open source node packages that already exist on npm.

Monday, 26 March 2018

Swift 4 - Adding Admob Interstitial Ads to your iOS App

  1. Sign up for or sign in to Admob
  2. Add an App in Admob
  3. Add an Ad unit in Admob - Choose Interstitial. You can set ad type, frequency capping and eCPM floor under advanced settings
  4. You should now have an Ad Unit ID and App Id which will be used in displaying interstitial ads to the user
  5. In your code, create a file InterstitialAd.swift, it should have the following code:
    
      let adController = InterstitialAd()
      import GoogleMobileAds
    
      class InterstitialAd : NSObject, GADInterstitialDelegate {
        var testAdId = "ca-app-pub-3940256099942544/4411468910"
        var adId = "[YOUR AD UNIT ID GOES HERE]"
        var interstitial : GADInterstitial!
        
        func createAndLoadInterstitial() {
            interstitial = GADInterstitial(adUnitID: testAdId)
            interstitial.delegate = self
            interstitial.load(GADRequest())
        }
        
        // Load new interstitial on close so that adController 
        // is ready for the next time showAd is called
        func interstitialDidDismissScreen(_ ad: GADInterstitial) {
            createAndLoadInterstitial()
        }
    
        func showAd(_ viewController: UIViewController) {
          if interstitial.isReady {
            interstitial.present(fromRootViewController: viewController)
          }
        }
      }
    
    
  6. Add the following to your AppDelegate.swift file, it will configure the app for Admob ads and load the first interstitial so that it is ready to be displayed when you try to display the interstitial:
    
      ...
      import GoogleMobileAds
    
      @UIApplicationMain
      class AppDelegate: UIResponder, UIApplicationDelegate {
    
        ...
        var appId = "[YOUR ADMOB APP ID GOES HERE]"
    
        func application(_ application: UIApplication, 
            didFinishLaunchingWithOptions launchOptions: 
            [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
          GADMobileAds.configure(withApplicationID: appId)
          adController.createAndLoadInterstitial()
    
          ...
        }
    
        ...
    
      }
    
    
  7. From the view controller where you want to show your interstitial ad, make the following call:
    
      adController.showAd(self)
    
    

Note:

For testing, you should use the testAdId as the adUnitID in InterstitialAd.swift, this is so that real ads are not displayed during testing. For release, switch to using adId.

... included for brevity in ApplicationDelegate.swift code above

Swift 4 - How to share messages and images

There are many benefits of enabling sharing within your application:
  • Free advertising for your app if users share from it on social media
  • Better user experience
  • Using recent iOS features to make the app feel more modern
The code for this is simple, when an event occurs that you would like to share on, you can call the following method:

  func share(_ message: String , shareImage: UIImage) {
    let share = [message, shareImage] as [Any]
    let activityViewController = UIActivityViewController(
      activityItems: share, applicationActivities: nil)
    activityViewController.popoverPresentationController?
      .sourceView = self.view
    self.present(activityViewController, animated: true, 
      completion: nil)
  }