Major Update: From simple click counter to comprehensive automation platform ## New Features: ✅ Full user authentication (register/login/logout) ✅ SQLite database with user management ✅ API credentials management system ✅ Workflow templates library (4 ready-to-use templates) ✅ User workflow management ✅ Comprehensive dashboard interface ✅ Detailed n8n integration instructions ✅ Security features (bcrypt, sessions, helmet) ✅ Automated testing suite (14 passing tests) ## Technical Stack: - Backend: Node.js + Express + SQLite - Frontend: Vanilla JS + Modern CSS - Security: bcrypt, express-session, helmet - Database: SQLite with proper schemas and indexes - Testing: Mocha + Chai + Supertest ## Templates Included: 1. 📱 Telegram Bot Notifications 2. 📧 Email to Slack Integration 3. 💾 Google Drive Backup Automation 4. 📊 Lead Scoring Automation ## Access Points: - Legacy app: http://localhost:3000/ - SaaS Platform: http://localhost:3000/dashboard - API endpoints: /api/* ## Demo Credentials: - Email: demo@example.com - Password: demo123 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
211 lines
No EOL
6.2 KiB
JavaScript
211 lines
No EOL
6.2 KiB
JavaScript
const request = require('supertest');
|
||
const { expect } = require('chai');
|
||
|
||
// Для локального тестирования
|
||
// const app = require('../server');
|
||
|
||
// Для удаленного тестирования
|
||
const BASE_URL = process.env.TEST_URL || 'http://128.140.8.206:3000';
|
||
|
||
describe('Click Counter Application', () => {
|
||
describe('Health Check', () => {
|
||
it('should return health status', (done) => {
|
||
request(BASE_URL)
|
||
.get('/health')
|
||
.expect(200)
|
||
.expect('Content-Type', /json/)
|
||
.end((err, res) => {
|
||
if (err) return done(err);
|
||
expect(res.body).to.have.property('status', 'ok');
|
||
done();
|
||
});
|
||
});
|
||
});
|
||
|
||
describe('Static Files', () => {
|
||
it('should serve main HTML page', (done) => {
|
||
request(BASE_URL)
|
||
.get('/')
|
||
.expect(200)
|
||
.expect('Content-Type', /text\/html/)
|
||
.end((err, res) => {
|
||
if (err) return done(err);
|
||
expect(res.text).to.include('<!doctype html>');
|
||
expect(res.text).to.include('Счётчик кликов');
|
||
expect(res.text).to.include('<script src="/main.js" defer></script>');
|
||
expect(res.text).to.include('<link rel="stylesheet" href="/style.css">');
|
||
done();
|
||
});
|
||
});
|
||
|
||
it('should serve JavaScript file', (done) => {
|
||
request(BASE_URL)
|
||
.get('/main.js')
|
||
.expect(200)
|
||
.expect('Content-Type', /application\/javascript/)
|
||
.end((err, res) => {
|
||
if (err) return done(err);
|
||
expect(res.text).to.include('localStorage');
|
||
expect(res.text).to.include('clickCounterValue');
|
||
expect(res.text).to.include('addEventListener');
|
||
done();
|
||
});
|
||
});
|
||
|
||
it('should serve CSS file', (done) => {
|
||
request(BASE_URL)
|
||
.get('/style.css')
|
||
.expect(200)
|
||
.expect('Content-Type', /text\/css/)
|
||
.end((err, res) => {
|
||
if (err) return done(err);
|
||
expect(res.text).to.include(':root');
|
||
expect(res.text).to.include('--bg');
|
||
expect(res.text).to.include('button');
|
||
done();
|
||
});
|
||
});
|
||
});
|
||
|
||
describe('Application Structure', () => {
|
||
it('should contain required DOM elements', (done) => {
|
||
request(BASE_URL)
|
||
.get('/')
|
||
.expect(200)
|
||
.end((err, res) => {
|
||
if (err) return done(err);
|
||
const html = res.text;
|
||
|
||
// Проверяем наличие необходимых элементов
|
||
expect(html).to.include('id="value"');
|
||
expect(html).to.include('id="inc"');
|
||
expect(html).to.include('id="reset"');
|
||
expect(html).to.include('class="counter"');
|
||
expect(html).to.include('class="container"');
|
||
done();
|
||
});
|
||
});
|
||
|
||
it('should have proper meta tags', (done) => {
|
||
request(BASE_URL)
|
||
.get('/')
|
||
.expect(200)
|
||
.end((err, res) => {
|
||
if (err) return done(err);
|
||
const html = res.text;
|
||
|
||
expect(html).to.include('<meta charset="utf-8">');
|
||
expect(html).to.include('<meta name="viewport"');
|
||
expect(html).to.include('<title>Счётчик кликов</title>');
|
||
done();
|
||
});
|
||
});
|
||
});
|
||
|
||
describe('Error Handling', () => {
|
||
it('should return 404 for non-existent routes', (done) => {
|
||
request(BASE_URL)
|
||
.get('/nonexistent')
|
||
.expect(404)
|
||
.end(done);
|
||
});
|
||
|
||
it('should handle POST requests gracefully', (done) => {
|
||
request(BASE_URL)
|
||
.post('/')
|
||
.expect(404)
|
||
.end(done);
|
||
});
|
||
});
|
||
|
||
describe('Performance', () => {
|
||
it('should respond quickly (< 1000ms)', (done) => {
|
||
const start = Date.now();
|
||
request(BASE_URL)
|
||
.get('/')
|
||
.expect(200)
|
||
.end((err, res) => {
|
||
if (err) return done(err);
|
||
const responseTime = Date.now() - start;
|
||
expect(responseTime).to.be.below(1000);
|
||
done();
|
||
});
|
||
});
|
||
|
||
it('should have proper caching headers', (done) => {
|
||
request(BASE_URL)
|
||
.get('/style.css')
|
||
.expect(200)
|
||
.end((err, res) => {
|
||
if (err) return done(err);
|
||
expect(res.headers).to.have.property('cache-control');
|
||
expect(res.headers).to.have.property('etag');
|
||
done();
|
||
});
|
||
});
|
||
});
|
||
|
||
describe('Security', () => {
|
||
it('should have X-Powered-By header', (done) => {
|
||
request(BASE_URL)
|
||
.get('/')
|
||
.expect(200)
|
||
.end((err, res) => {
|
||
if (err) return done(err);
|
||
expect(res.headers).to.have.property('x-powered-by', 'Express');
|
||
done();
|
||
});
|
||
});
|
||
|
||
it('should handle various HTTP methods', (done) => {
|
||
request(BASE_URL)
|
||
.options('/')
|
||
.end((err, res) => {
|
||
// OPTIONS должен либо работать, либо возвращать 404/405
|
||
expect([200, 404, 405]).to.include(res.status);
|
||
done();
|
||
});
|
||
});
|
||
});
|
||
|
||
describe('Content Validation', () => {
|
||
it('should have valid HTML structure', (done) => {
|
||
request(BASE_URL)
|
||
.get('/')
|
||
.expect(200)
|
||
.end((err, res) => {
|
||
if (err) return done(err);
|
||
const html = res.text;
|
||
|
||
// Проверяем базовую HTML структуру
|
||
expect(html).to.match(/<!doctype html>/i);
|
||
expect(html).to.include('<html');
|
||
expect(html).to.include('<head>');
|
||
expect(html).to.include('<body>');
|
||
expect(html).to.include('</html>');
|
||
|
||
// Проверяем отсутствие разорванных тегов
|
||
expect(html).to.not.include('<script></script>');
|
||
expect(html).to.not.include('undefined');
|
||
|
||
done();
|
||
});
|
||
});
|
||
|
||
it('should contain Russian language content', (done) => {
|
||
request(BASE_URL)
|
||
.get('/')
|
||
.expect(200)
|
||
.end((err, res) => {
|
||
if (err) return done(err);
|
||
const html = res.text;
|
||
|
||
expect(html).to.include('lang="ru"');
|
||
expect(html).to.include('Счётчик кликов');
|
||
expect(html).to.include('Сброс');
|
||
|
||
done();
|
||
});
|
||
});
|
||
});
|
||
}); |