Orchid is a framework that extends the Jasmine/Protractor test environment written in Node.js. Orchid lets you write semantic reusable tests that make your tests read like QA use cases and introduces reuasable code blocks and tests.

Sample Code

- Extends Jasmine.

- Works with Protractor.

- Reuse your tests.

- Reads like a use case.
            
require('orchid-js')();

Test('that a new feature works',function(){
  By('navigating to the page',function(){
    It('should navigate to my url',function(url){
        browser.get(url);
    })('https://mywebsite');
  })();
  Then('clicking all the buttons',function(){
    It('should click a button',function(id){
        button = element(by.id( id ));
        button.click();
        expect(button.isPresent()).toBeTruthy();
    })( 'button1' );
    // Tests are reusable!
    It('should click a button')( 'button2' );
    It('should click a button')( 'button3' );
    It('should click a button')( 'button4' );
  })();
  Then('navigating to the next page',function(){
    // Let's reuse the navigation code
    It('should navigate to my url')('https://mywebsite/page2');
  })();
  Then('testing the feature works on this page too',function(){
    // We'll reuse the entire 'clicking all the buttons' block!
    By('clicking all the buttons')();
  })();
})();
            
          

Setup

Use npm to install Orchid in your project

npm install orchid-js

Then call Orchid at the beginning of your tests.

require('orchid-js')();

Orchid extends Jasmine and works with Protractor.

We also recommend using Jasmine Spec Reporter to get a live report of your tests as they execute in the terminal.

How to write a test

You should be familiar with writing Jasmine tests.
            
describe('test a new feature',function(){
  it('should click a button',function(){
      button = element(by.id('button1'));
      button.click();
      expect(button.isPresent()).toBeTruthy();
  });
});
            
          
In Orchid you would write the test like this...
            
Describe('test a new feature',function(){
  It('should click a button',function(){
      button = element(by.id('button1'));
      button.click();
      expect(button.isPresent()).toBeTruthy();
  });
});
            
          
...however, in Orchid, the passed function doesn't automatically execute. Instead the function is returned, and then called. Note the () on the end of the Orchid function.
            
// Jasmine
describe('test a new feature',function(){});

// Orchid
Describe('test a new feature',function(){})();
            
          
So, in Orchid, the correct way to write the test is like this.
            
Describe('test a new feature',function(){
  It('should click a button',function(){
      button = element(by.id('button1'));
      button.click();
      expect(button.isPresent()).toBeTruthy();
  })();
})();
            
          
This is what allows Orchid tests to be reusable. For example, we could now setup our "It" function to accept an "id" as a parameter.
            
  It('should click a button',function(id){
      button = element(by.id( id ));
      button.click();
      expect(button.isPresent()).toBeTruthy();
  })( 'button1' );
            
          
And then we can call that function multiple times!
            
  It('should click a button',function(id){
      button = element(by.id( id ));
      button.click();
      expect(button.isPresent()).toBeTruthy();
  })( 'button1' );

  It('should click a button')( 'button2' );
  It('should click a button')( 'button3' );
  It('should click a button')( 'button4' );
  It('should click a button')( 'button5' );
            
          

Semantics

Orchid uses several functions that do exactly the same thing, but are used simply to help the tests read more like a QA use case.
            
// These functions do exactly the same thing, 
// and can be used interchangeably

Describe('',function(){...})();
Test('',function(){...})();
By('',function(){...})();
Then('',function(){...})();
And('',function(){...})();
            
          
They can be used interchangeably when reusing tests.
            
// These functions do exactly the same thing, 
// and can be used interchangeably

Test('these semantic functions',function(){
  By('logging this',function(str){
    It('should log the string',function(){
      console.log(str)
    })();
  })('this works');
  Then('logging this')('this works');
  And('logging this')('this works');
  Describe('logging this')('this works');
  // These all point to the first function 
  // that defined 'logging this'!
})();
            
          
Like in Jasmine, these semantic functions can be nested, but ultimately will need an "It" function in them to pass as a test.
            
Test('',function(){
  By('',function(str){
    // this It needs to be here
    It('',function(){})();
  })();
})();
            
          

Reusable tests

In Orchid we can use tests that are defined in other files.
            
// A new test file

Test('that a new feature works',require('../newFeatureTest'))();
            
          
...when that test is exported as a function using Node.js modules.
            
// newFeatureTest.js

module.exports = function(){              
require('orchid-js')();

Test('that a new feature works',function(){
  By('navigating to the page',function(){
    It('should navigate to my url',function(url){
        browser.get(url);
    })('https://mywebsite');
  })();
  Then('clicking all the buttons',function(){
    It('should click a button',function(id){
        button = element(by.id( id ));
        button.click();
        expect(button.isPresent()).toBeTruthy();
    })( 'button1' );
    // Tests are reusable!
    It('should click a button')( 'button2' );
    It('should click a button')( 'button3' );
    It('should click a button')( 'button4' );
  })();
  Then('navigating to the next page',function(){
    // Let's reuse the navigation code
    It('should navigate to my url')('https://mywebsite/page2');
  })();
  Then('testing the feature works on this page too',function(){
    // We'll reuse the entire 'clicking all the buttons' block!
    By('clicking all the buttons')();
  })();
})();
}
            
          
You can also pass parameters to required tests.
            
// passing parameters to a require function

username = 'testuser';
password = 'password1234';
Test('the login',require('../login'))(username,password);
            
          
 
            
// login.js

module.exports = function(username, password){              
require('orchid-js')();

Test('that the login works',function(){
  By('logging in as '+username+' with '+password,function(){
    It('should login the user',function(url){
        element(by.id('usernameField')).sendKeys(username);
        element(by.id('passwordField')).sendKeys(password);
        element(by.id('loginButton')).click();
    })();
  })();
})();
}
            
          


Reusable tests with semantics make very clean, readable, and maintainable tests.

            
// example test using require

Test('that the new footer updates with the shopping cart',function(){
  By('logging into the website',require('../websiteLogin'))();
  And('navigating to the shopping cart view',require('../navigateToShoppingCart'))();
  Then('adding items to your shopping cart',require('../addItemsToCart'))();
  And('verifying that the new footer updates',function(){
    
    ...
    
  })();
})();              
            
          

Working With Variables

In Jasmine and Orchid, it/It functions are chained together with promises, maintaining scope.

So a variable that is changed in a previous it/It function will be accessable in future it/It functions.
            

var foo = "unchanged";

Describe('change foo',function(){
    It('should change foo',function(){
        foo = "changed";
    })();
})();

Describe('print foo',function(){
    It('should print foo',function(){
        console.log(foo);
    })();
})();

// logs "changed"
            
          
However, when working with reusable tests in Orchid, you will notice that the scope inside the it/It functions are not recognized when passed from the outside Describe functions.

This is because the Describe functions look for that variable in their outside scope. In this case, "unchanged".
            

var foo = "unchanged";

Describe('change foo',function(){
    It('should change foo',function(){
        foo = "changed";
    })();
})();

Describe('print what is passed in',function(str){
    It('should print what is passed in',function(str){
        console.log(str);
    })(str);
})(foo);

// logs "unchanged"
            
          
The solution is to use Javascript object literals.

Pass in the name of the object's property that you want to reference or modify, and then call that object's property inside the It scope.

This maintains a global scope environment in your tests.
            

var foo = {};
foo['bar'] = "unchanged";

Describe('change a foo property',function(){
    It('should change a foo property',function(){
        foo['bar'] = "changed";
    })();
})();

Describe('print a foo property',function(prop){
    It('should print a foo property',function(prop){
        console.log(foo[prop]);
    })(prop);
})('bar');

/* Notice that you do not pass in foo['bar']
// because that would look for foo['bar] on the outside scope,
// Instead you pass in 'bar' and then look up foo['bar'] inside 
// the It function's scope.
*/

// logs "changed"

Describe("print an object's property",function(obj,prop){
    It("should print an object's property",function(obj,prop){
        console.log(obj[prop]);
    })(obj,prop);
})(foo,'bar');

// logs "changed"

            
          

Authors and Contributors

Authored by Marshall Bean @webdesignerkits and maintained by @orchid-js

Contributors: Sean Christensen @StashRaptor, Tyson Hughes @tysonweir