Create an admin panel with Node.js and AdminBro

Reading Time: 4 minutes

What’s great about Node.js is the huge economy of useful packages. For example, AdminBro is a package for creating admin interfaces that can be plugged into your application.

You provide database models or schemas (like blog posts, user comments, etc.) and AdminBro generates the user interface for you. You can manage content through this user interface and talk straight to your database.

What is AdminBro?

AdminBro is an open-source package from Software Brothers that adds an auto-generated admin panel to your Node.js application.

You can connect your various databases to the admin interface and perform standard CRUD operations (create, read, update, delete) on the records. This greatly simplifies and extends your ability to find, monitor, and update your app data across multiple sources.

Creating the admin panel

First of all, we need to create a new folder that will hold our app. Then we will open the terminal in that folder and run:

npm init

Go through all the steps and initialize the Node.js application and a package.json -file will be created:

Then, we need to install some dependencies express and the express-formidable packages. express-formidable is a peer dependency for AdminBro:

npm install express express-formidable

Then we can install the AdminBro and the AdminBro express plug-in:

npm install admin-bro @admin-bro/express

Now we will create a new file index.js and inside we can create an express router that will handle all AdminBro routes:

const AdminBro = require('admin-bro')
const AdminBroExpress = require('@admin-bro/express')

const express = require('express')
const app = express()

const adminBro = new AdminBro ({
    Databases: [],
    rootPath: ‘/admin’,
})

const router = AdminBroExpress.buildRouter (adminBro)

The next step is to set up the router as middleware using the Express.js app object:

app.use(adminBro.options.rootPath, router)
  
app.listen(3000, ()=> {
  console.log('Application is up and running under localhost:3000/admin')
})

And that’s it! You successfully set up the dashboard! Run:

node index.js

And go ahead and head over to the http://localhost:3000/admin path. The dashboard should be ready and working.

Installing the Database Adapter and Adding Resources

AdminBro can be connected to many different types of resources. Right now, they support the following options:

To add resources to AdminBro, you first have to register an adapter for the resource you want to use. Let’s go with the mongoose solution for now and install the required dependencies:

npm install mongoose @admin-bro/mongoose

Then we register the adapter so that it can be used in our project:

const AdminBroMongoose = require('@admin-bro/mongoose')

AdminBro.registerAdapter(AdminBroMongoose)

Now we can make a connection to the database and pass the resources:

const mongoose = require('mongoose')

const connection = await mongoose.connect('mongodb://localhost:27017/users', {useNewUrlParser: true, useUnifiedTopology: true})

const User = mongoose.model('User', { name: String, email: String, surname: String })

const adminBro = new AdminBro ({
  Databases: [connection],
  rootPath: '/admin',
  resources: [User]
})

Finishing up

Now let’s put all together and our index.js should look like this:

const AdminBro = require('admin-bro')
const AdminBroExpress = require('@admin-bro/express')
const AdminBroMongoose = require('@admin-bro/mongoose')

const express = require('express')
const app = express()

const mongoose = require('mongoose')

AdminBro.registerAdapter(AdminBroMongoose)

const run = async () => {
  const connection = await mongoose.connect('mongodb://localhost:27017/users', {useNewUrlParser: true, useUnifiedTopology: true})

  const User = mongoose.model('User', { name: String, email: String, surname: String })

  const adminBro = new AdminBro ({
    Databases: [connection],
    rootPath: '/admin',
    resources: [User]
  })
  const router = AdminBroExpress.buildRouter(adminBro)
  app.use(adminBro.options.rootPath, router)
    
  app.listen(3000, ()=> {
    console.log('Application is up and running under localhost:3000/admin')
  })
}

run()

At this point, we have basically built the admin interface. To verify that we have done everything correctly, first make sure your database is up and then re-run the server:

node index.js

Go to http://localhost:3000/admin and on the left side you can see your first model:

Summary

These are the basic steps to create an admin panel from scratch with Node.js and AdminBro. You can go deeper, you can customize your panel resources and widgets, add validation to the fields, configure role-based access control and much more. Any questions? You can check out the AdminBro docs for more details.

Taiko, useful toy for automation testing

Reading Time: 6 minutes

Every day we are implementing new features/client requirements. On every release, we want those changes to be correct, previous features should still be working… with other words we want a stable application. That is why it’s necessary for the BE and FE to write tests (unit and integration tests).

The best way is to have regression end to end automation tests. But it is not always fun to write them. Sometimes it is complex, takes some time so that we are avoiding writing them. Sometimes if a workload is larger it requires a dedicated team with QA to cover all this work. Furthermore to follow all changes and adapt existing tests etc.

There are a few tools that make all this work easier. Browser robots that record actions on the web pages, some frameworks that offer good and easy ways of writing automation tests, but either they are too difficult to learn and sometimes hard to use or they are not for free.

That is why I chose Taiko, a free and open source browser test automation framework that makes all this work easy to do. A few features that are crucial for writing end to end automated tests in my opinion are:

  • Easy setup
  • Interactive recording
  • Smart selectors
  • Easy integration with Cauge

The best way to present all this is to go through some simple examples. I’ll use http://saucedemo.com/ to write a simple test for adding items in the cart.

I want almost everyone to be able to write tests, it does not have to be a complex procedure to set it up. Taiko is a free open source node.js library and it works with chromium-based browsers. Tests are written in JavaScript or any language that compiles to JavaScript (TypeScript).

This means to start to write a test with Taiko we will need a pre-installed node.js. It is a straightforward setup. (https://nodejs.org/en/download/).

For the given example I used a power shell on windows but you can write it in any terminal application that you are familiar with. The command to install taiko is:

npm install -g taiko

After successful installation of taiko we will run REPL prompt:

npx taiko

Here are two important features:

  • Interactive recorder. It means that taiko will archive all successful commands that we are going to write here
  • And the second one is the use of Taiko’s API. We can list all available APIs with command
.api

or

.api <api>

All these API references are online too: https://docs.taiko.dev/api/reference.

Simple example

Let’s write one basic test for http://saucedemo.com/. By writing following commands in the prompt we will assure that saucedemo login, adding a product to cart and basket works:

await openBrowser();

// opens a new browser, I had chromium and it was open without any other setup because it uses Chrome DevTools Protocol instead of WebDriver…

await goto("saucedemo.com");

// navigates to / opens the web page that we want to test

var password = await text("_", below("Password for")).text();

In this line of code we have a few key commands:

  • var password – we will take the data (password) from and use it to log in
  • text – selector – which identifies an element on the page, it will look for text element with text to match with. In our example, it will be “_”
  • below – proximity selector – it makes relative HTML element search. It will search for elements under bellow “Password for” on the page

var usernames = await text("_", below("usernames")).text();
console.log(usernames);

// as it is js we can use this command too; it will be archived. I used it to check the values, it can be removed from the final script

var username = usernames.split("\n")[1];
console.log(username);
var password = passwords.split("\n")[1];
console.log(password);
await write(username, into(textBox({id: "user-name"})));

After the username and password are read from page, we will log in:

  • write – command that types given text into the given or focused element
  • into  – selector for the element to write text into
  • textBox – selector for a text field for input, selecting it with some attribute. In our case, it will be id, but it can be any attribute too
await write(password, into(textBox({id: "password"})));
await click("LOGIN");

// again smart selector, it automatically looks for and clicks button login

Since there are more products, we want to test a specific one, we will use proximity selector to add a specific product to the cart. If we don’t add “toRightOf” it will click the first component with “ADD TO CART” label on it.

await click("ADD TO CART", toRightOf("$9.99"));
await click("ADD TO CART", toRightOf("$15.99"));

To assure that ADD TO CART functionality works, we will check the basket, if the wanted products are there:

await click(link({class: "shopping_cart_link"}));

Assertions are made with every command, looking for any element. For example command

await click("ADD TO CART", toRightOf("$9.998"));

will throw an error

[FAIL] Error: Element with text $9.998 not found, run `.trace` for more info.

but if we want to make some customer check then we can use any node.js assertions:

assert.strictEqual(await text("9.99").exists(), true);
assert.strictEqual(await text("15.99").exists(), true);
await click("menu");
await click("Logout");

With all these commands we created one basic test scenario. All these commands are already archived and we can write them in js file to execute this test anytime:

.code testAddCart.js

And exit the recording session:

.exit

Running our previous test with:

npx taiko testAddCart.js

Other possibilities

Tests can be grouped and run with test runners. There are three that are supported Gauge, Mocha, Jest. Try it with Cauge, it is an easy straightforward procedure to set it up. Like that, by using Gauge, we can integrate these tests to build pipeline in Jenkins.

Conclusion

The setup is simple, very easy and fast.

The interactive way of writing tests, seeing the result of every command in real-time is very good for learning the library. You don’t have to make a write build run, just write it in REPL and that’s it, you see the results.

But the moment of selecting elements on the page was not so satisfying. Smart selectors are not so smart when there are more of the same elements. You have to go again with XPath or class and have to debug the page and check the code for attributes and values.