Jest test passes locally, but why does it fail when running npm test in GitHub actions? - jestjs

I'm having an issue with certain Jest tests in the GitHub CI. My project is in TypeScript so I'm using ts-jest. Here is the function I'm testing, it sets the "text" fields of date and time elements:
const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
export const setDateAndTime = (dateDisplay: TextElement, clockDisplay: TextElement): void => {
let now: Date = new Date(Date.now());
dateDisplay.text = `${days[now.getDay()]}, ${months[now.getMonth()]} ${now.getDate()}, ${now.getFullYear()}`;
let hours: number = preferences.clockDisplay === "12h" ? now.getHours() % 12 || 12 : now.getHours();
let minutes: number = now.getMinutes();
clockDisplay.text = minutes < 10 ? `${hours}:0${minutes}` : `${hours}:${minutes}`;
};
Here is a test for that function:
import { TestElement } from "../mocks/test-element";
let dateDisplay = new TestElement() as TextElement;
let clockDisplay = new TestElement() as TextElement;
test("Sets date and time display correctly", () => {
jest.spyOn(Date, "now").mockImplementation(() => 1607913488);
setDateAndTime(dateDisplay, clockDisplay);
expect(dateDisplay.text).toBe("Mon, Jan 19, 1970");
expect(clockDisplay.text).toBe("9:38");
});
TestElement is just a dummy element with a "text" field:
export class TestElement {
text = "";
}
Locally, both of the expect() statements pass. But, in GitHub, I get the following error, for the second statement only:
TypeError: (0 , _jestDiff.diffStringsRaw) is not a function
18 | setDateAndTime(dateDisplay, clockDisplay);
19 | expect(dateDisplay.text).toBe("Mon, Jan 19, 1970");
> 20 | expect(clockDisplay.text).toBe("9:38");
| ^
21 | });
Since the issue is happening only in GitHub, I'll post my node.js.yml configuration as well:
name: Node.js CI
on: [push]
jobs:
build:
runs-on: windows-latest
strategy:
matrix:
node-version: [14.x]
steps:
- uses: actions/checkout#v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node#v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npx fitbit-build
- run: npm test
For the life of me, I can't figure out why only the second string comparison fails - it's the exact same function performed on the exact same class of element. After doing some research, the only thing I can find is that diffStringsRaw is used internally by Jest through the jest-diff package, but I haven't gotten much farther than that. Any help would be much appreciated!

Another way to fix this, if you didn't want to use the GitHub Action, is to replace your test command with
TZ=Europe/London npm test, where Europe/London is your desired time zone.
See this link for discussion

The answer to this wasn't in Jest at all - rather it was that the GitHub test runner runs in GMT, not the local time zone, which was why the first expect() passed for comparing the date, and the second one failed for comparing the time.
For anyone interested, the possible solutions are:
Refactor your function or your test to account for the system time zone; or
Add a GitHub action to your configuration that explicitly sets the time zone.
I chose option 2 and went with Setup Timezone (shoutout to zcong1993 for the easy to use action!)

Related

How to iterate all the variables from a variables template file in azure pipeline?

test_env_template.yml
variables:
- name: DB_HOSTNAME
value: 10.123.56.222
- name: DB_PORTNUMBER
value: 1521
- name: USERNAME
value: TEST
- name: PASSWORD
value: TEST
- name: SCHEMANAME
value: SCHEMA
- name: ACTIVEMQNAME
value: 10.123.56.223
- name: ACTIVEMQPORT
value: 8161
and many more variables in the list.
I wanted to iterate through all the variables in the test_env_template.yml using a loop to replace the values in a file, Is there a way to do that rather than calling each values separately like ${{ variables.ACTIVEMQNAME}} as the no. of variables in the template is dynamic.
In short no. There is no easy way to get your azure pipeline variables specific to tamplet variable. You can get env variables, but there you will get regular env variables and pipeline variables mapped to env variables.
You can get it via env | sort but I'm pretty sure that this is not waht you want.
You can't display variables specific to template but you can get all pipeline variables in this way:
steps:
- pwsh:
Write-Host "${{ convertToJson(variables) }}"
and then you will get
{
system: build,
system.hosttype: build,
system.servertype: Hosted,
system.culture: en-US,
system.collectionId: be1a2b52-5ed1-4713-8508-ed226307f634,
system.collectionUri: https://dev.azure.com/thecodemanual/,
system.teamFoundationCollectionUri: https://dev.azure.com/thecodemanual/,
system.taskDefinitionsUri: https://dev.azure.com/thecodemanual/,
system.pipelineStartTime: 2021-09-21 08:06:07+00:00,
system.teamProject: DevOps Manual,
system.teamProjectId: 4fa6b279-3db9-4cb0-aab8-e06c2ad550b2,
system.definitionId: 275,
build.definitionName: kmadof.devops-manual 123 ,
build.definitionVersion: 1,
build.queuedBy: Krzysztof Madej,
build.queuedById: daec281a-9c41-4c66-91b0-8146285ccdcb,
build.requestedFor: Krzysztof Madej,
build.requestedForId: daec281a-9c41-4c66-91b0-8146285ccdcb,
build.requestedForEmail: krzysztof.madej#hotmail.com,
build.sourceVersion: 583a276cd9a0f5bf664b4b128f6ad45de1592b14,
build.sourceBranch: refs/heads/master,
build.sourceBranchName: master,
build.reason: Manual,
system.pullRequest.isFork: False,
system.jobParallelismTag: Public,
system.enableAccessToken: SecretVariable,
DB_HOSTNAME: 10.123.56.222,
DB_PORTNUMBER: 1521,
USERNAME: TEST,
PASSWORD: TEST,
SCHEMANAME: SCHEMA,
ACTIVEMQNAME: 10.123.56.223,
ACTIVEMQPORT: 8161
}
If you prefix them then you can try to filter using jq.

Github action write to a repo in Node with #actions/core or #actions/github?

Learning Github Actions I'm finally able to call an action from a secondary repo, example:
org/action-playground
.github/workflows/test.yml
name: Test Write Action
on:
push:
branches: [main]
jobs:
test_node_works:
runs-on: ubuntu-latest
name: Test if Node works
strategy:
matrix:
node-version: [12.x]
steps:
- uses: actions/checkout#v2
with:
repository: org/write-file-action
ref: main
token: ${{ secrets.ACTION_TOKEN }} # stored in GitHub secrets created from profile settings
args: 'TESTING'
- name: action step
uses: ./ # Uses an action in the root directory
id: foo
with:
who-to-greet: 'Darth Vader'
- name: output time
run: |
echo "The details are ${{ steps.foo.outputs.repo }}"
echo "The time was ${{ steps.foo.outputs.time }}"
echo "time: ${{ steps.foo.outputs.time }}" >> ./foo.md
shell: bash
and the action is a success.
org/write-file-action
action.yml:
## https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions
name: 'Write File Action'
description: 'workflow testing'
inputs:
who-to-greet: # id of input
description: 'Who to greet'
required: true
default: './'
outputs:
time: # id of output
description: 'The time we greeted you'
repo:
description: 'user and repo'
runs:
using: 'node12'
main: 'dist/index.js'
branding:
color: 'green'
icon: 'truck' ## https://docs.github.com/en/actions/creating-actions/metadata-syntax-for-github-actions#brandingicon
index.js that is built to dist/index.js
fs = require('fs')
const core = require('#actions/core')
const github = require('#actions/github')
try {
// `who-to-greet` input defined in action metadata file
const nameToGreet = core.getInput('who-to-greet')
console.log(`Hello ${nameToGreet}!`)
const time = new Date().toTimeString()
core.setOutput('time', time)
const repo = github.context.payload.repository.full_name
console.log(`full name: ${repo}!`)
core.setOutput('repo', repo)
// Get the JSON webhook payload for the event that triggered the workflow
const payload = JSON.stringify(github.context.payload, undefined, 2)
console.log(`The event payload: ${payload}`)
fs.writeFileSync('payload.json', payload) // Doesn't write to repo
} catch (error) {
core.setFailed(error.message)
}
package.json:
{
"name": "wite-file-action",
"version": "1.0.0",
"description": "workflow testing",
"main": "dist/index.js",
"scripts": {
"build": "ncc build ./index.js"
},
"dependencies": {
"#actions/core": "^1.4.0",
"#actions/github": "^5.0.0"
},
"devDependencies": {
"#vercel/ncc": "^0.28.6",
"prettier": "^2.3.2"
}
}
but at current workflow nothing is created in action-playground. The only way I'm able to write to the repo is from a module using the API with github-api with something like:
const GitHub = require('github-api')
const gh = new GitHub({
token: config.app.git_token,
}, githubUrl)
const repo = gh.getRepo(config.app.repoOwner, config.app.repoName)
const branch = config.app.repoBranch
const path = 'README.md'
const content = '#Foo Bar\nthis is foo bar'
const message = 'add foo bar to the readme'
const options = {}
repo.writeFile(
branch,
path,
content,
message,
options
).then((r) => {
console.log(r)
})
and passing in the repo, org or user from github.context.payload. My end goal is to eventually read to see if it exists, if so overwrite and write to README.md a badge dynamically:
`![${github.context.payload.workflow}](https://github.com/${github.context.payload.user}/${github.context.payload.repo}/actions/workflows/${github.context.payload.workflow}.yml/badge.svg?branch=main)`
Second goal from this is to create a markdown file (like foo.md or payload.json) but I cant run an echo command from the action to write to the repo, which I get is Bash and not Node.
Is there a way without using the API to write to a repo that is calling the action with Node? Is this only available with Bash when using run:
- name: output
shell: bash
run: |
echo "time: ${{ steps.foo.outputs.time }}" >> ./time.md
If so how to do it?
Research:
Passing variable argument to .ps1 script not working from Github Actions
How to pass variable between two successive GitHub Actions jobs?
GitHub Action: Pass Environment Variable to into Action using PowerShell
How to create outputs on GitHub actions from bash scripts?
Self-updating GitHub Profile README with JavaScript
Workflow syntax for GitHub Actions

GitHub Action using npx fails with message /usr/bin/env: 'node': No such file or directory

I'm creating a nodejs GitHub Action that relies on npx to run semantic-release:
src/index.ts (extract, bundled to dist/index.js)
import * as core from '#actions/core'
import * as exec from '#actions/exec'
import * as github from '#actions/github'
;(async () => {
const githubRegistry: string = `https://npm.pkg.github.com/${github.context.repo.owner}`
const githubToken: string = core.getInput('github_token', { required: true })
await exec.exec('npx', ['semantic-release'], {
NPM_CONFIG_REGISTRY: githubRegistry,
NPM_TOKEN: githubToken,
GITHUB_TOKEN: githubToken
})
})()
action.yml (extract)
inputs:
github_token:
required: true
runs:
using: node12
main: dist/index.js
.github/workflows/release.yml
on:
push:
branches:
- main
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout#v2
- uses: actions/setup-node#v1
with:
node-version: 12
- run: yarn
- uses: ./
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
When the release workflow is ran on GitHub, it fails with the following output:
/opt/hostedtoolcache/node/12.19.0/x64/bin/npx semantic-release
/usr/bin/env: 'node': No such file or directory
Error: The process '/opt/hostedtoolcache/node/12.19.0/x64/bin/npx' failed with exit code 127
It looks like npx is trying to run node but could not find it.
I would appreciate any help, thanks :)

Parameterisation of a groovy Pipeline for use with jenkins

I have a groovy pipeline which I've inherited from a project that I forked.
I wish to pass in Jenkins Choice Parameters as a Parameterised build. At present, I only wish to expose the environment in which to run ( but will want to parameterise further at a later stage), such that a user can choose it from the Jenkins dropdown and use on demand.
I used the snippet generator to help.
Can someone please help with the syntax? I am using Node with a package.json to run a script and with to pass in either dev or uat:
properties([[$class: 'BuildConfigProjectProperty', name: '', namespace: '', resourceVersion: '', uid: ''], parameters([choice(choices: 'e2e\nuat', description: 'environment ', name: 'env')])])
node('de-no1') {
try {
stage('DEV: Initialise') {
git url: 'https://myrepo.org/mycompany/create-new-user.git', branch: 'master', credentialsId: CREDENTIALS_ID
}
stage('DEV: Install Dependencies') {
sh 'npm install'
}
stage('${params.env}: Generate new users') {
sh 'npm run generate:${params.env}'
archiveArtifacts artifacts: '{params.env}-userids.txt', fingerprint: true
}
This currently fails with:
npm ERR! missing script: generate:{params.env}
Assume you want to replace ${params.env} with a value when you call npm?
If this is the case, you need to use double quotes " to let Groovy know you will be doing String templating...ie:
sh "npm run generate:${params.env}"

Can't set environment variables for container in ApplicationLoadBalancedFargateService

(CDK 1.18.0 and Python 3.6)
task_role = iam.Role(
self,
id=f"...",
assumed_by=iam.ServicePrincipal("ecs-tasks.amazonaws.com"),
managed_policies=[...]
)
repo = get_repo(self)
task_def = ecs.FargateTaskDefinition(
self,
"...",
memory_limit_mib=30720,
cpu=4096,
task_role=task_role,
execution_role=self.ecs_execution_role,
)
cont = task_def.add_container(
"...",
image=ecs.ContainerImage.from_ecr_repository(repo),
logging=ecs.LogDrivers.aws_logs(stream_prefix=f"Logging"),
command=["bash", "start.sh"],
environment={"NAME1": 'VALUE1', "NAME2": 'VALUE2'} # what would I have to put here?
)
cont.add_port_mappings(ecs.PortMapping(container_port=8080))
fg = ecsp.ApplicationLoadBalancedFargateService(
self,
"...",
task_definition=task_def,
assign_public_ip=True,
)
I want to pass NAME1=VALUE1 and NAME2=VALUE2 to the container.
I tried various ways to express the environment variables. But none worked. Am I doing something fundamentally wrong here?
Other than this specific issue the service deploys and runs.
The approach you follow seems to work here on the latest version (1.23.0). But I could not find any hint in the release notes why this might have changed. Can you update to the latest version?
task_def.add_container("container", environment={"a": "b", "c": "d"}, image=aws_ecs.ContainerImage.from_registry(name="TestImage"), memory_limit_mib=512)
newtask1C300F30:
Type: AWS::ECS::TaskDefinition
Properties:
ContainerDefinitions:
- Environment:
- Name: a
Value: b
- Name: c
Value: d
Essential: true
Image: TestImage
Memory: 512
Name: container

Resources