Skip to main content

5 posts tagged with "javascript"

View All Tags

· 3 min read

Fix: Missing favicon.ico in Sencha ExtJS production builds

Fixing missing favicon.ico in Ext JS applications.

The original issue is tracked here: https://github.com/conjoon/conjoon/issues/24

The issue

I’m not quite sure when and why it broke, but it looks like production builds of Sencha Ext JS applications do not contain any favicon.ico originally existing in the development build (anymore).

While everything seems to be okay with development builds (that’s easy, they refer to the development’s root folder in most cases and do not copy and move files around like the production build does), deploying to production will show the default icon coming with your vendor’s browser for any Sencha ExtJS application, at least when your environment is using the following package versions:

    webpack v4.39.3n/a
ext-webpack-plugin v7.6.0
Ext JS v7.6.0.41
Sencha Cmd v7.6.0.87
``

A call to

```bash
$ cross-env webpack
--env.profile=desktop
--env.environment=production
--env.treeshake=yes
--env.cmdopts=--uses

will produce the production build, but the production build will not contain the favicon.ico. Here’s proof (… sort of):

The fix

I did not invest too much investigating the reason why this file is omitted. Instead, I added the copy-webpack-plugin to the project and made sure the the favicon is copied over when running npm run build.

If you’re reading this post, you most likely stumbled across the same issue. Here’s how to fix it.

  1. Add copy-webpack-plugin to your project

I’m still sporting webpack v4.39.3 so I had to fall back to an older version of the plugin. I’m using copy-webpack-plugin@6.4.1 in this case:

    $ npm i -D copy-webpack-plugin@6.4.1
  1. Add a few more modules to your project’s webpack.config.js

Add the following two lines to the head of the file:

    // ....
const CopyWebpackPlugin = require("copy-webpack-plugin");
const fs = require("fs");
//...

Why fs? I couldn’t find an easy way to access the application’s name at this point of the build step through the variables available, so I’m using fs to parse the project's app.json. The value of its name-property is then used for computing the destination folder for the copy-operation.

  1. Add the copy-webpack-plugin to the list of plugins in the script

You’ll easily find the location that has to be edited when looking for the @sencha/ext-webpack-plugin-configuration:

    const plugins = [
new ExtWebpackPlugin({
// ...
}),
new CopyWebpackPlugin({
patterns: [{
from: path.resolve(__dirname, './favicon.ico'),
to: path.join(
__dirname,
"build",
environment,
JSON.parse(
fs.readFileSync(
path.resolve(__dirname, './app.json')
)
).name
)
}]
})
]

Subsequent builds will now contain the favicon.ico.

· 3 min read

Easily create Siesta tests for your applications

Siesta is a JavaScript unit and UI testing tool originally written by Mats Bryntse which allows for running tests (for Ext JS (view-) components, amongst others) directly in the web browser (or headless in case you want to use it with your ci tools).

It is conjoon’s favorite among the various testing tools out there (we’re also working with Jest🃏 when there’s no Ext JS involved) and without it, it’s clear that End-to-End tests of some of the features and changes that have made it into conjoon would have slowed the project down due to their cyclic complexity — or even worse, make the software stuck in regression.

extjs-app-webmail alone sports more than 5000 unit and ui tests created with Siesta and they all make sure that the frontend behaves as intended and is free from unwelcomed side effects for any edge case that might occur (we know that’s a bold statement).

To ease the process of setting up a functional Siesta environment, we’re introducing the cli tool create-siesta which can be used with any JavaScript framework that requires a functional Siesta application running in a web browser, or at least a fully fledged infrastructure for running Siesta tests. However, by providing integrated build options for the Ext JS SDK, it is best suited for environments where the Sencha framework is already being used or will be used.

For using the tool, all that is required is a working Node.js installation on your machine. The scaffolding process can then be started by typing

$ npx create-siesta@latest

on the console. create-siesta will then guide you through the installation process and also consider the environment (aka current working directory) it was invoked in, by falling back to already existing Ext JS sources for example, or any other package requirements already available.

Once create-siesta is finished, a folder (defaulting to tests) will be available with your project that contains a scaffolded Siesta environment with templates for additional and future tests you and your team can implement. Tests can then be started with

$ npm run siesta:test

create-siesta builds upon an already available helper tool for creating tests with Siesta, namely **@coon-js/siesta-lib-helper which is already widely used throughout the conjoon** project and the packages it depends on. This means that you will have an additional control at hand with the Siesta application that allows for switching between toolkit dependent tests and dynamically changing timeout values used with

    t.waitForMs()

in tests.

*The additional control made available by siesta-lib-helper allows for switching between the modern M and classic C toolkit and provides a list of timeout values that can be globally used with t.waitForMs() in tests.*

Documentation

The documentation for create-siesta can be found here, the sources are available with the coon.js organization at GitHub, which provides a collection of useful tools for rapid Sencha Ext JS application development, and spawned from the conjoon open source project.

· One min read

JavaScript, function-as-object and the internet

The internet ™️ has caught up on an ad by FERCHAU, found — amongst others — somewhere within the depths of the Berlin subway.

Some devs cringe at the code used with the ad — turns out it can easily get de-mystified with JavaScript’s function-as-object style:

    const careerStuck = () => {};
careerStuck.stop = () => {};

Opinions regarding semantics may differ:

    if (careerStuck() === true) {
careerStuck.stop();
beSmart(); // 👀
}

Further reading

Martin Fowler on FunctionAsObject in an article from 2017. The pattern goes back to the last century when Eugene Wallingford coined the name “Function as Object” in his 1999 pattern language “Envoy”.

· 2 min read

conjoon installer updates

Creating a custom labeled version of conjoon has never been easier.

The installer (i.e. initializer) for conjoon received an update to make setting up a local installation of the frontend more convenient and more intuitive.

The installer requires Node.js 16.14 or above and is started by typing

$ npx create-conjoon@latest

on the command line. Additionally, you have the option to initiate the quick install right away by providing the name and the target directory for the installation:

$ npx create-conjoon@latest "my webmail" ~/webmail

The quick install will continue the installation process by picking the latest release from the package registry (including pre-releases) and configure the installation with fake endpoints returning demo data. You can still connect to production systems later on by using appropriate settings.

If you prefer a guided installation for configuring endpoints for existing backends right away, you can omit any of the arguments. The installer will then present various options to make sure your requirements are met!

In both cases, target directory will contain the files required for developing conjoon and a production build available in the build folder, which is served by typing npm run stage in the target directory.

· 3 min read

Fix: Ext JS Simlets and omitting the status code

I recently stumbled upon a mean error that was hard to track down. Luckily, the issue was caused by a simple carelessness regarding fully configured response-objects of Simlets used in the dev-environment of the conjoon-project and did not require any large refactoring.

The original issue is tracked here: https://github.com/conjoon/extjs-app-webmail/issues/196

The issue

The problem was related to batch-operations in Ext JS: When any operation of the batch fails, the pauseonexception-setting is considered and further processing of any remaining operation is halted. The user then has the option to retry the last failed operation, continuing with the remaining operations if this was finally successful. However, retrying some of the failed operations gave the following error:

The operation that failed — and which is now about to be retried — was already destroyed by Ext JS internal garbage-collector.

The fix

It took some time to find out that the operation was destroyed and that some of the properties required for re-running it were already de-referenced. This took me to the internals of response-handling of XMLHttpRequests of Ext JS and finally to the code where the HTTP status of a request was inspected. Take note that I’m mainly developing with Simlets to avoid costly network roundtrips to any backend.

It is mandatory for the Simlets to properly treat mocked backend-errors by also providing the appropriate status-code.

It was assumed that simply setting the success-property in any of the delete/put/...-methods of a simlet treating the request would later on be parsed internally; setting the status-code of the response automatically to anything other than 200. That is not the case. It is mandatory for the Simlets to properly treat mocked backend-errors by also providing the appropriate status-code.

The flaw was too often overseen since the status-field was not set, as the following code examples show (examples represent code in any of the delete/post/put/...-methods of a Json-Simlet; see Ext.ux.ajax.Simlet):

The following leaves the ret.status-property undefined:

    //....
const ret = {};
ret.responseText = Ext.JSON.encode({
success: false
});
return ret;

… while the following re-uses the predefined value of 200 for the status, even if success=false should indicate that the request could not be processed as expected:

    const 
me = this,
ret = {};

Ext.Array.forEach(me.responseProps, function (prop) {
if (prop in me) {
ret[prop] = me[prop];
}
});

ret.responseText = Ext.JSON.encode({
success: false
});

return ret;

In both cases, Ext.data.request.Ajax would parse the response and its status in the onComplete-method:

    result = Ext.data.request.Ajax.parseStatus(xhr.status, xhr);

An undefined status would cause the response to be treated as not successful (example 1), while anything other than undefined would be parsed and interpreted according to its HTTP-status representative (successful: 200; failure: 404, 500 etc.).

ExtJS would assume that a successful operation is not needed anymore; it is automatically destroyed then. This was unexpected behavior in this case, since the code marked the operation as a failure, but not via the status-code, which was required.