Cake O'Clock project

In this project, I don’t understand this syntax because we haven’t learned this syntax in previous lessons.

Can you post a screenshot of the syntax you don’t understand? (I don’t have PRO so I can’t see the lesson).

user-visits-index-test.js:

const {assert} = require('chai');
const {jsdom} = require('jsdom');

const parseTextFromHTML = (htmlAsString, selector) => {
  const selectedElement = jsdom(htmlAsString).querySelector(selector);
  if (selectedElement !== null) {
    return selectedElement.textContent;
  } else {
    throw new Error(`No element with selector ${selector} found in HTML string`);
  }
};

describe('User visits index', () => {
  describe('to post an order', () => {
    it('starts with a blank order', () => {
      browser.url('/');

      assert.equal(browser.getText('#deliver-to span'), '');
      assert.equal(browser.getText('#cake-type span'), '');
      assert.equal(browser.getText('#fillings span'), '');
      assert.equal(browser.getText('#size span'), '');
    });

    // Add the 'outside of working hours' test here
  it('does not provide options outside of working hours', () => {
      const earlyHr = '7:00';
      const lateHr = '13:00';
 
      browser.url('/');
      const HTML = browser.getHTML('body');
      const parsedHTML = parseTextFromHTML(HTML, '#select-pickUp');
 
      assert.notInclude(parsedHTML, earlyHr);
      assert.notInclude(parsedHTML, lateHr);
    });

    // Add the 'displays the selected hour' test here
    it('displays the selected hour', () => {
      const hour = '9:00';
 
      browser.url('/');
      browser.selectByVisibleText('#select-pickUp', hour);
      browser.click('#submit-order');
      browser.url('/');
 
      assert.include(browser.getText('#pickUp'), hour);
    });
    
    // Add the 'labels the pick up hour' test here
 it('labels the pick up hour correctly', () => {
      const label = 'pick up time:';
 
      browser.url('/');
      const HTML = browser.getHTML('body');
      const parsedHTML = parseTextFromHTML(HTML, '#pickUp');
 
      assert.include(parsedHTML, label);
    });

    it('accepts the customer name', () => {
      const name = 'Hungry Person';

      browser.url('/');
      browser.setValue('#name', name);
      browser.click('#submit-order');
      browser.url('/');

      assert.include(browser.getText('#deliver-to'), name);
    });

    it('accepts the cake type', () => {
      const cakeType = 'Whole Wheat';

      browser.url('/');
      browser.click('#whole-wheat');
      browser.click('#submit-order');
      browser.url('/');

      assert.include(browser.getText('#cake-type'), cakeType);
    });

    it('accepts multiple fillings', () => {
      const firstChoice = 'Strawberries';
      const secondChoice = 'Banana';

      browser.url('/');
      browser.click('#strawberries');
      browser.click('#banana');
      browser.click('#submit-order');
      browser.url('/');

      assert.include(browser.getText('#fillings'), firstChoice);
      assert.include(browser.getText('#fillings'), secondChoice);
    });

    it('accepts the stack size', () => {
      const optionText = 'Double Stack';
      const optionNum = '2';

      browser.url('/');
      browser.selectByVisibleText('#select-stack', optionText)
      browser.click('#submit-order');
      browser.url('/');

      assert.include(browser.getText('#size'), optionNum);
    });
  });

  describe('to clear an order', () => {
    it('deletes the selected options', () => {
      const name = 'Indecisive Person';
      const time = '10:00';

      browser.url('/');
      browser.setValue('#name', name);
      browser.selectByVisibleText('#select-pickUp', time)
      browser.click('#submit-order');
      browser.click('#clear-order');
      browser.url('/');

      assert.equal(browser.getText('#deliver-to span'), '');
      assert.equal(browser.getText('#cake-type span'), '');
      assert.equal(browser.getText('#fillings span'), '');
      assert.equal(browser.getText('#size span'), '');
    });
  });
});

order-test.js:

const Order = require('../../models/order');
const {assert} = require('chai');
const {mongoose, databaseUrl, options} = require('../../database');

describe('Order', () => {
  beforeEach(async () => {
    await mongoose.connect(databaseUrl, options);
    await mongoose.connection.db.dropDatabase();
  });

  afterEach(async () => {
    await mongoose.disconnect();
  });

  describe('.updateOrCreate', () => {
    describe('when a record already exists', () =>{
      it('updates the record', async () => {
        const partialOrder = {
          name: 'Regular Joe',
          cakeType: 'Plain',
          size: '2',
          pickUp: '9:00',
        };
        const update = ['Apple', 'Bacon', 'Chocolate Chips'];
        const existingOrder = await Order.create(partialOrder);

        const updatedOrder = await Order.updateOrCreate({fillings: update});

        const allOrders = await Order.find({});
        assert.equal(allOrders.length, 1);
        // toObject resolves issues with mongoose metadata in arrays
        assert.deepEqual(updatedOrder.fillings.toObject(), update);
        // check remaining fields
        assert.include(updatedOrder, partialOrder);
      });
    });

    describe('when a record does not exist', () =>{
      it('creates the record', async () => {
        let healthyOrder = {
          name: 'Healthy Person',
          cakeType: 'Whole Wheat',
          fillings: ['Macadamia Nuts'],
          size: '1',
          pickUp: '11:00',
        }

        const order = await Order.updateOrCreate(healthyOrder);

        const allOrders = await Order.find({});
        assert.equal(allOrders.length, 1);
        assert.deepEqual(allOrders[0].fillings.toObject(), healthyOrder.fillings);
        delete healthyOrder.fillings;
        assert.include(allOrders[0], healthyOrder);
      });
    });
  });

  describe('#cakeType', () => {
    it('is a String', () => {
      const nameAsAnInt = 1;

      const order = new Order({ cakeType: nameAsAnInt });

      assert.strictEqual(order.cakeType, nameAsAnInt.toString());
    });
  });

  describe('#name', () => {
    it('is a String', () => {
      const nameAsAnInt = 1;

      const order = new Order({ name: nameAsAnInt });

      assert.strictEqual(order.name, nameAsAnInt.toString());
    });
  });

  describe('#fillings', () => {
    it('is an Array', () => {
      const fillings = ['Apple', 'Bacon'];

      const order = new Order({fillings});

      // toObject resolves issues with mongoose metadata
      assert.deepEqual(order.fillings.toObject(), fillings);
    });
  });

  describe('#size', () => {
    it('is a String', () => {
      const sizeAsAnInt = 3;

      const order = new Order({size: sizeAsAnInt});

      assert.strictEqual(order.size, sizeAsAnInt.toString());
    });
  });


});

Oh, sorry, this is beyond my knowledge of JS, so I won’t be much help if you want an explanation.


@selectall, do you have any insights into explanations here?

1 Like

I’m not entirely sure which JavaScript syntax wouldn’t have been covered. I believe callbacks, arrow functions, etc. should have been in the path already. I do know it’s using libraries that haven’t been explained much yet, which might be what you’re meaning.

This project does seem to come a little early in the path. I know the same thing happened in the old Web Development path that I took.

jsdom is covered a bit more in the Learn Server Testing with TDD part of that track
chai and mocha are covered more in the upcoming lessons

If you’re not comfortable looking at the documentation, then I suggest coming back to this project after you’ve gone though some of the subsequent lessons.

I’m in the same boat. The lesson didn’t really explain how to actually construct unit tests, and when you run into it using all the stuff from mocha, like ‘describe’, ‘it’, and ‘assert’, it’s baffling.

As far as I can figure out, the testing uses Mocha, which is a testing framework, and what allows you to be using all those terms. It lets you run the tests. Then, on top of that, you’re using two additional things which are the ones ‘required’ at the top of the code, ‘chai’, and ‘jsdom’.

Chai adds to Mocha, and it is an assertion library, and it is what lets you write parts like:

assert.equal(browser.getText('#deliver-to span'), '');

Which basically means: is ‘browser.getText(‘deliver-to span’)’ equal to the part after the comma, a null string.

JSDOM is a super commonly used library that lets you interact with html DOM elements outside of a browser (Like, say, if you were trying to do a bunch of tests on a HTML file through node, like we are doing). It lets you actually look at stuff in particular DOM elements. Again, it’s the first time it’s dropped in, and not explained, so the stuff that JSDOM adds confused me also. Stuff like the jsdom(htmlAsString) which seems to be what actually works on your html file to allow you to pull stuff out of it using things like queryselector. As far as I can tell, all the browser.whatever lines are also added by JSDOM, allowing you to represent things like mouse clicks, like in the line:

browser.click('#submit-order');

The entire opening section:

const parseTextFromHTML = (htmlAsString, selector) => {
  const selectedElement = jsdom(htmlAsString).querySelector(selector);
  if (selectedElement !== null) {
    return selectedElement.textContent;
  } else {
    throw new Error(`No element with selector ${selector} found in HTML string`);
  }
};

exists to allow you to pull out relevant parts of the html and check against it, if it exists.

So, when you get into the actual tests, you have describe(a,b); where a is a description (as a string) and b is a function containing your unit tests, or further ‘describe’ lines to split your tests up even more.

The functions here are written as arrow functions ( () => {} ) but it looks like they don’t have to be. Some other examples(https://semaphoreci.com/community/tutorials/getting-started-with-node-js-and-mocha) use function() {}.

Then, the lines that start with ‘it’ are the actual unit tests.

it(a,b); 

Where ‘a’ is the description of the test, and b is the anonymous function doing the test.

So the ‘assert’ lines seem to be using functionality added by Chai. For example:

assert.equal(a,b);

will pass if a and b are equal. ‘a’ is the thing you’re checking. ‘b’ is what you want it to be equal to.
It looks like the ‘browser.getText(’#deliver-to span’)’ returns the text content of the span tag that is a child of the tag with ID ‘#deliver-to’. So your ‘assertion’ is that this should be a null string. If it it, this unit test will pass.

The first section that you write yourself when you’re doing the project is the part that includes the line:

      assert.notInclude(parsedHTML, earlyHr);

Which is still using the ‘assert’ from Chai, but uses the ‘notInclude’, as you want to make sure parsedHTML does not include earlyHr. It’s written this way to make it easier to follow, but it looks like you could still write it all out as:

      assert.notInclude(parseTextFromHTML(browser.getHTML('body'), '#select-pickUp'), earlyHr);

Chai seems to also include other asserts you can use, like deep.equal to check that arrays and subarrays all match up, for example.