Jasmine: Mocking ESM imports
In a Jasmine spec you want to spy on a function that is imported by the code under test. This card explores various methods to achieve this.
Example
We are going to use the same example to demonstrate the different approaches of mocking an imported function.
We have a module 'lib'
that exports a function hello()
:
// lib.js
function hello() {
console.log("hi world")
}
export hello
We have a second module 'client'
that exports a function helloTwice()
. All this does is call hello()
...
Regular Expressions: Quantifier modes
When you repeat a subpattern with a *
, +
or {...}
operator, you may choose between greedy, lazy and possessive modes.
Switching modes may affect the result and performance of your regular expressions. In the worst case, an ill-suited mode may make your regular expression so slow that it can DoS your application (Examples are the ActiveRecord's PostgreSQL CVE-2021-22880 or the [Cloudflare outage 2019](https://makandracards.com/makandra/77515-regular-expressions-excessive-backtracking...
SameSite cookies
TL;DR Most web applications do not require action on this. SameSite=None
(old browser default) will continue to work, and SameSite=Lax
(new Chrome default, gradually rolled out) is an even better default for cookies. Set SameSite=Strict
only for extra security in special cases (see below). If your application is rendered in an iframe (e.g. a video player or some news stream), you need to configure its relevant cookies as SameSite=None
.
The SameSite
cookie attribute targets **c...
How to make changes to a Ruby gem (as a Rails developer)
At makandra, we've built a few gems over the years. Some of these are quite popular: spreewald (> 1M downloads), active_type (> 1M downloads), and geordi (> 200k downloads)
Developing a Ruby gem is different from developing Rails applications, with the biggest difference: there is no Rails. This means:
- no defined structure (neither for code nor directories)
- no autoloading of classes, i.e. you need to
require
all files yourself - no
active_support
niceties
Also, their scope...
Rails asset pipeline: Using ESNext without a transpiler
If your app does not need to support IE11, you can use most ES6 features without a build step. Just deliver your plain JavaScript without transpilation through Babel or TypeScript, and modern browsers will run them natively.
Features supported by all modern browsers include:
- fat arrow functions (
() => { expr }
) -
let
/const
class
-
async
/await
- Promises
- Generators
- Symbols
- Rest arguments (
...args
) - Destructuring
You won't be able to use import
and export
, or use npm modules.
See this [ES6 compatibility mat...
A reasonable default CSP for Rails projects
Every modern Rails app should have a Content Security Policy enabled.
Very compatible default
The following "default" is a minimal policy that should
- "just work" for almost all applications
- give you most of the benefits of a CSP
In your config/initializers/content_security_policy.rb
, set
Rails.application.config.content_security_policy do |policy|
policy.object_src :none
policy.script_src :unsafe_eval, :strict_dynamic, :https # Browsers with support for "'strict-dynamic'" will ignore "https:"
po...
How to configure Selenium WebDriver to not automatically close alerts or other browser dialogs
tl;dr
We recommend configuring Selenium's unhandled prompt behavior to
{ default: 'ignore' }
with the monkey patch below.
When running tests in a real browser, we use Selenium. Each browser is controlled by a specific driver, e.g. Selenium::WebDriver::Chrome
for Chrome.
There is one quirk to all drivers (at least those following the W3C webdriver spec) that can be impractical:
When any user prompt (like an alert
) is encountered when trying to perform an action, they will [...
Vortrag: Content Security Policy: Eine Einführung
Grundidee
CSP hat zum Ziel einen Browser-seitigen Mechanismus zu schaffen um einige Angriffe auf Webseiten zu verhindern, hauptsächlich XSS-Angriffe.
Einschub: Was ist XSS?
XSS = Cross Site Scripting. Passiert wenn ein User ungefiltertes HTML in die Webseite einfügen kann.
<div class="comment">
Danke für den interessanten Beitrag! <script>alert('you have been hacked')</script>
</div>
Rails löst das Problem weitgehend, aber
- Programmierfehler weiter möglich
- manchmal Sicherheitslücken in Gems oder Rails
Lösungsid...
Matching line feeds with regular expressions works differently in every language
Although regular expression syntax is 99% interchangeable between languages, keep this in mind:
- By default, the dot character (
"."
) does not match a line feed (newline, line break,"\n"
) in any language. - Some languages allow you to modify the behavior of a regular expression by appending a modifier to the pattern expression. E.g.
/foo/i
makes the pattern case-insensitive in many languages. Note however that some of these modifiers may not exist or mean entirely different things in different languages. - Some languages have a m...
Capybara will not find links without an href attribute
Capybara and most assistive technology will fail to find <a>
tags that are missing an href
attribute. This will probably happen to you every now and then on JavaScript-heavy applications.
An example would be an AngularJS application where the following HTML actually works. [1]
<a ng-click="hello()">Hello</a>
Capybara will fail to find that link, even though looking it up via the DOM shows it:
>> find_link("Hello")
Capybara::...
Always disable autocomplete for date pickers
When we write a form with date fields, we often use graphical data picker like Rome to get a consistent calendar popup on all browsers.
When you integrate a date picker popup, remember to also set autocomplete="off"
on the text input that opens the calendar on click
. Otherwise the autocomplete suggestions will cover the calendar box and make it unusable:
If you are using a tool like Unpoly you might want to set autocomplete="off"
i...
Rails asset pipeline: Why relative paths can work in development, but break in production
The problem
When using the asset pipeline your assets (images, javascripts, stylesheets, fonts) live in folders inside app
:
app/assets/fonts
app/assets/images
app/assets/javascripts
app/assets/stylesheets
With the asset pipeline, you can use the full power of Ruby to generate assets. E.g. you can have ERB tags in your Javascript. Or you can have an ERB template which generates Haml which generates HTML. You can chain as many preprocessors as you want.
When you deploy, Rails runs assets:precompile
...
HTTP 302 redirects for PATCH or DELETE will not redirect with GET
A HTTP 302 Found
redirect to PATCH
and DELETE
requests will be followed with PATCH
or DELETE
. Redirect responses to GET
and POST
will be followed with a GET
. The Rails form_for
helper will use a workaround to send POST
requests with a _method
param to avoid this issue for PATCH
/DELETE
.
If you make requests yourself, watch out for the following behavior.
When you make an AJAX request PATCH /foo
and the /foo
action redirects to /bar
, browsers will request PATCH /bar
. You probably expected the second requ...
Ensure passing Jasmine specs from your Ruby E2E tests
Jasmine is a great way to unit test your JavaScript components without writing an expensive end-to-end test for every small requirement.
After we integrated Jasmine into a Rails app we often add an E2E test that opens that Jasmine runner and expects all specs to pass. This way we see Jasmine failures in our regular test runs.
RSpec
In a [feature spec](https://web.archive.org/web/20150201092849/http://www.rel...
Regular tasks for long-running projects
When projects run for many years, they require special regular maintenance to stay fresh. This kind of maintenance is usually not necessary for small or quick projects, but required to keep long-running projects from getting stale.
You should be able to fit this into a regular development block.
Quarterly
Check which libraries need updating
As time goes by, libraries outdate. Check your software components and decide if any of it needs an update. Your main components (e.g. Ruby, Rails, Unpoly) should always be reasonably up to da...
Use -webkit-line-clamp to natively truncate long (multi-line) texts with an ellipsis
Note: You won't need this for single lines of text. In this case it is better to just use the text-overflow
property: Use CSS "text-overflow" to truncate long texts
You can use -webkit-line-clamp
in your CSS/SASS to natively render an ellipsis (...
) after a specific amount of lines for a multi-line text in your HTML.
Earlier, it was necessary to implement JavaScript solutions like Superclamp.js to enable this because the browser support has been rather limited...
Error handling in DOM event listeners
When an event listener on a DOM element throws an error, that error will be silenced and not interrupt your program.
In particular, other event listeners will still be called even after a previous listener threw an error. Also the function that emitted the event (like element.dispatchEvent()
or up.emit()
) will not throw
either.
In the following example two handlers are listening to the foo
event. The first handler crashes, th...
RubyMine: Efficiently filtering results in the "Finder" overlay
RubyMine comes with a nice way to grep through your project's files: The finder (ctrl + shift + f
). Don't be discouraged about the notice 100+ matches in n+ files
if your searched keyword is too general or widely used in your project.
RubyMine comes with a few ways to narrow down the resulting list, don't hesitate to apply those filters to speed up your search. Your keybinding might vary based on your personal settings.
File mask (alt + k
)
If you already know the file extension of your ...
The developer console can do more than you think!
You can do so much more than console.log(...)
! See the attached link for a great breakdown of what the developer console can give you.
Some of my favorites:
console.log takes many arguments
E.g. console.log("Current string:", string, "Current number:", 12)
Your output can have hyperlinks to Javascript objects
E.g. console.log("Check out the current %o, it's great", location)
[Di...
Preventing users from uploading malicious content
When you allow file uploads in your app, a user might upload content that hurts other users.
Our primary concern here is users uploading .html
or .svg
files that can run JavaScript and possibly hijack another user's session.
A secondary concern is that malicious users can upload executables (like an .exe
or .scr
file) and use your server to distribute it. However, modern operating systems usually warn before executing files that were downloaded from t...
Capybara: Preventing server errors from failing your test
When your Rails application server raises error, Capybara will fail your test when it clears the session after the last step. The effect is a test that passes all steps, but fails anyway.
Capybara's behavior will help you to detect and fix errors in your application code. However, sometimes your application will explode with an error outside your control. Two examples:
- A JavaScript library references a source map in its build, but forgets to package the source map
- CarrierWave cleans up an upload or cache file after the record was delet...
Better numeric inputs in desktop browsers
You want to use <input type="number">
fields in your applications.
However, your desktop users may encounter some weird quirks:
- Aside from allowing only digits and decimal separators, an "e" is also allowed (to allow scientific notation like "1e3").
- Non-technical users will be confused by this.
- Your server needs to understand that syntax. If it converts only digits (e.g.
to_i
in Ruby) you'll end up with wrong values (like 1 instead o...
How to create memory leaks in jQuery
jQuery doesn't store information about event listeners and data
values with the element itself. This information is instead stored in a global, internal jQuery cache object. Every time you add an event listener or data value to a jQuery object, the jQuery cache gains another entry.
The only way that a jQuery cache entry gets deleted is when you call remove()
on the element that put it there!
Since cache entries also have a pointer back to the element that spawned them, it is easy to create DOM elements that can never be garbage-co...
Google Analytics: Changing the tracked URL path
By default, Google Analytics tracks the current URL for every request. Sometimes you will want to track another URL instead, for example:
- When an application URL contains a secret (e.g. an access token)
- When you want to track multiple URLs under the same bucket
- When you want to track interactions that don't have a corresponding URL + request (e.g. a Javascript button or a PDF download)
Luckily the Analytics code snippet allows you to freely choose what path is being tracked. Simple change this:
ga('send', 'pageview');
......