When writing unit tests for JavaScript code relying on Moment.js or Day.js (a lightweight alternative to the former, with pretty much the same API) to handle date-related shenanigans, one might find themselves in need to mock their library of choice when its main function is used with no parameters, to obtain a representation of current time.
The mock we actually need to set up in such a case is actually a partial one: we want it to act as a proxy for parameterized calls, passing them down to the original implementation, while returning a fixed value otherwise. Thankfully, Jest grants us the possibility to do so fairly easily:
As you can see, the aforementioned original implementation is available from within jest.mock's scope through the use of jest.requireActual, allowing us to check if it was called with or without parameters, and pass a fixed value in the latter case.
Despite the rise of ES6 and especially the sacrosanct fat arrow syntax, JavaScript developers might still find themselves in need for Function.prototype.bind in some edge cases; binding all functions in an object to the same this value regardless of depth is one of them.
The example I have in mind is the building of a Vue.js plugin such as this one:
Fairly logically, setting it up through Vue.use(MyPlugin) and calling this.$myPlugin.aMethod and this.$myPlugin.yet.anotherMethod yields a reference to the plugin object itself twice, which might not be what you want depending on what the plugin is supposed to do.
In such a situation, we therefore need to write a recursive binding function, which luckily is not all that hard:
For any given prop, you can specify the expected type, and whether the prop is required or has a default value. Doing so is actually best practice, just like defining data as a function in your components.
Regarding Object props, however, there is no built-in mechanism allowing one to validate the object's keys or structure; the closest thing we have is the ability to pass in a custom validation function, so let's build upon that!
The most simple implementation could be along the lines of the following:
That pretty much does it! A more thorough version could support deep key checking with a dotted notation, using this package for example.
But let's go further! If you find yourself using this pattern on a regular basis, why would you not write a mixin - actually, a mixin factory - to make it easier?
Using such stuff gives us the confidence to reference keys in Object props directly, without defensively checking for their presence in our code. If you have other ways to deal with this kind of thing, please let me know!
If you are developing your own frontend JavaScript packages, chances are you use ES6 syntax in your codebase, especially the long-awaited module system. Browser compatibility is pretty much painless thanks to Babel, but things can get harder if you want your package to expose a CLI script (to generate code or whatever) that is meant to be ran through Node.js directly.
ESM to the rescue! Add esm to your project's package.json as a dependency (or peerDependency if you want to leave its installation up to your package's consumer), and your users will then be able to run your script with node -r esm node_modules/path/to/script.js.
The only detail to pay attention to is to account for the difference between ES6's module system and CommonJS's:
const someES6Module = require("./path/to/module").default; // explicitly require default export if that is what you need
const namedExport = require("./path/to/module").namedExport; // named exports are to be referenced this way
// Or just use destructuring:
const { default: someES6Module, namedExport } = require("./path/to/module");
Hello there! It's been a while since I posted anything, but today I'd like to take a moment to tell you about a simple yet quite efficient pattern we've been using at my current job regarding Vue.js applications.
As with any frontend framework, Vue.js isn't really opinionated regarding how you're supposed to inject configuration into your SPA, assuming this configuration comes from your backend service. A common pattern in this era of API-centric services is to dedicate a JSON endpoint to configuration that your JavaScript code will fetch immediately upon boot. This allows us to keep all backend considerations aside, but has several disadvantages as well:
limited flexibility if the network or your backend itself fail (the more you can provide for an offline and/or degraded experience, the better)
somewhat extraneous network round-trip for something you could have provided locally to begin with
your JS code still needs to know which URL to request, which is already configuration; how do you provide that?
For all these reasons, we have begun to experiment with a little something we like to call the widget pattern. Nothing crazy here: we essentially wrap the new Vue declaration inside a build function, which is then called from public/index.html:
We then use its second argument in src/main.js to inject config into our app, which we can variabilize depending on the environment if we need to, by swapping wildcards with environment variables from Docker for example:
import Vue from "vue";
import App from "./components/App";
export default App; // more on that below
export function build(el, config) {
new Vue({
el,
render: h => h(App, { props: /* use config here */ }),
});
delete window.App; // you may want to dereference the module afterwards
}
All we have to do to make this work is to instruct Webpack to expose our root module as window.App, which happens in vue.config.js:
This gives us the flexibility we need, and forces us to think about our app's root component's input, as its props are used to receive the aforementioned configuration values. We thus also made an habit of exporting this root component as default, which means we can use our app on its own as before or use it as a "widget" (hence the "pattern" name) in a larger Vue.js codebase or a legacy application.
If you're using something similar or have something to say about this way of doing things, use the comment section below!