| name | evernote-ci-integration |
| description | Configure CI/CD pipelines for Evernote integrations.
Use when setting up automated testing, continuous integration,
or deployment pipelines for Evernote projects.
Trigger with phrases like "evernote ci", "evernote github actions",
"evernote pipeline", "automate evernote tests".
|
| allowed-tools | Read, Write, Edit, Bash(npm:*), Grep |
| version | 1.0.0 |
| license | MIT |
| author | Jeremy Longshore <jeremy@intentsolutions.io> |
Evernote CI Integration
Overview
Configure continuous integration pipelines for Evernote integrations, including test automation, credential management, and deployment workflows.
Prerequisites
- Git repository set up
- CI/CD platform (GitHub Actions, GitLab CI, etc.)
- Test suite implemented
- Sandbox API credentials
Instructions
Step 1: GitHub Actions Workflow
name: Evernote Integration CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
NODE_VERSION: '20'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
unit-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm run test:unit
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
integration-tests:
runs-on: ubuntu-latest
needs: [lint, unit-tests]
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run integration tests
env:
EVERNOTE_CONSUMER_KEY: ${{ secrets.EVERNOTE_CONSUMER_KEY }}
EVERNOTE_CONSUMER_SECRET: ${{ secrets.EVERNOTE_CONSUMER_SECRET }}
EVERNOTE_ACCESS_TOKEN: ${{ secrets.EVERNOTE_SANDBOX_TOKEN }}
EVERNOTE_SANDBOX: 'true'
run: npm run test:integration
security-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Snyk security scan
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
Step 2: Test Configuration
module.exports = {
testEnvironment: 'node',
testMatch: [
'**/tests/**/*.test.js'
],
collectCoverageFrom: [
'src/**/*.js',
'!src/**/*.test.js'
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
},
setupFilesAfterEnv: ['./tests/setup.js'],
projects: [
{
displayName: 'unit',
testMatch: ['<rootDir>/tests/unit/**/*.test.js']
},
{
displayName: 'integration',
testMatch: ['<rootDir>/tests/integration/**/*.test.js'],
testTimeout: 30000
}
]
};
const dotenv = require('dotenv');
dotenv.config({ path: '.env.test' });
beforeAll(() => {
if (process.env.NODE_ENV === 'production') {
throw new Error('Cannot run tests in production environment');
}
if (process.env.EVERNOTE_SANDBOX !== 'true') {
console.warn('WARNING: EVERNOTE_SANDBOX should be true for tests');
}
});
afterAll(async () => {
});
Step 3: Mock Evernote Client for Unit Tests
class MockNoteStore {
constructor() {
this.notebooks = [];
this.notes = [];
this.tags = [];
}
async listNotebooks() {
return this.notebooks;
}
async createNotebook(notebook) {
const created = {
...notebook,
guid: `notebook-${Date.now()}`,
created: Date.now(),
updated: Date.now()
};
this.notebooks.push(created);
return created;
}
async listTags() {
return this.tags;
}
async createNote(note) {
if (!note.content.includes('<?xml version="1.0"')) {
const error = new ();
error. = ;
error. = ;
error;
}
created = {
...note,
: ,
: .(),
: .()
};
..(created);
created;
}
() {
note = ..( n. === guid);
(!note) {
error = ();
error. = ;
error. = guid;
error;
}
note;
}
() {
filtered = [....];
(filter.) {
words = filter..();
filtered = filtered.(
n..().(words) ||
n..().(words)
);
}
{
: filtered.(offset, offset + maxNotes),
: filtered.
};
}
() {
. = [];
. = [];
. = [];
}
}
{
() {
{
: ,
: ,
: ,
: ,
: ,
: .() - ,
: .(),
: ,
: {
: ,
: ,
: .() + *
}
};
}
}
{
() {
. = options;
. = ();
. = ();
}
() {
.;
}
() {
.;
}
() {
(, , );
}
() {
;
}
() {
(, , , {
: (.() + )
});
}
}
. = {
,
,
};
Step 4: Unit Test Examples
const { MockEvernoteClient } = require('../mocks/evernote-mock');
const NoteService = require('../../src/services/note-service');
describe('NoteService', () => {
let noteService;
let mockClient;
beforeEach(() => {
mockClient = new MockEvernoteClient();
noteService = new NoteService(mockClient.getNoteStore());
});
describe('createNote', () => {
it('should create a note with valid ENML', async () => {
const result = await noteService.createNote({
title: 'Test Note',
content: '<p>Test content</p>'
});
expect(result.guid).toBeDefined();
expect(result.title).toBe('Test Note');
});
it('should sanitize note title', async () => {
const result = await noteService.createNote({
: ,
:
});
(result.)..();
});
(, () => {
badService = {
: mockClient.(),
: (params) => {
note = { : params., : params. };
mockClient.().(note);
}
};
(
badService.({
: ,
:
})
)..({
: ,
:
});
});
});
(, {
( () => {
noteService.({ : , : });
noteService.({ : , : });
});
(, () => {
results = noteService.();
(results..).();
(results.[].).();
});
});
});
Step 5: Integration Test Examples
const Evernote = require('evernote');
const hasCredentials = process.env.EVERNOTE_ACCESS_TOKEN;
(hasCredentials ? describe : describe.skip)('Evernote API Integration', () => {
let client;
let noteStore;
let createdNoteGuids = [];
beforeAll(() => {
client = new Evernote.Client({
token: process.env.EVERNOTE_ACCESS_TOKEN,
sandbox: process.env.EVERNOTE_SANDBOX === 'true'
});
noteStore = client.getNoteStore();
});
afterAll(async () => {
for (const guid of createdNoteGuids) {
try {
await noteStore.deleteNote(guid);
} catch (error) {
console.log(`Cleanup: Could not delete ${guid}`);
}
}
});
it('should authenticate and get user info', async () => {
const userStore = client.();
user = userStore.();
(user.).();
(user.).();
});
(, () => {
notebooks = noteStore.();
(.(notebooks)).();
(notebooks.).();
});
(, () => {
testTitle = ;
content = ;
note = ..();
note. = testTitle;
note. = content;
created = noteStore.(note);
createdNoteGuids.(created.);
(created.).();
(created.).(testTitle);
retrieved = noteStore.(created., , , , );
(retrieved.).();
});
(, () => {
promises = [];
( i = ; i < ; i++) {
promises.(noteStore.());
}
results = .(promises);
(results.( .(r))).();
}, );
});
Step 6: Secrets Management
jobs:
deploy-production:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Deploy
env:
EVERNOTE_CONSUMER_KEY: ${{ secrets.EVERNOTE_PROD_CONSUMER_KEY }}
EVERNOTE_CONSUMER_SECRET: ${{ secrets.EVERNOTE_PROD_CONSUMER_SECRET }}
run: npm run deploy
Step 7: Package.json Scripts
{
"scripts": {
"test": "jest",
"test:unit": "jest --selectProjects unit",
"test:integration": "jest --selectProjects integration --runInBand",
"test:coverage": "jest --coverage",
"lint": "eslint src/ tests/",
"lint:fix": "eslint src/ tests/ --fix",
"typecheck": "tsc --noEmit",
"ci": "npm run lint && npm run typecheck && npm run test:unit",
"ci:full": "npm run ci && npm run test:integration"
}
}
Output
- GitHub Actions workflow for Evernote CI
- Comprehensive test configuration
- Mock client for unit testing
- Integration test examples
- Secrets management setup
Best Practices
| Practice | Reason |
|---|
| Run unit tests on every PR | Fast feedback, no API usage |
| Limit integration tests | Preserve rate limits |
| Use sandbox for all CI | Never test against production |
| Store secrets securely | Protect API credentials |
| Clean up test data | Don't leave garbage in sandbox |
Resources
Next Steps
For deployment pipelines, see evernote-deploy-integration.