1
0
Fork 0
mirror of https://github.com/betaflight/betaflight-configurator.git synced 2025-07-23 16:25:22 +03:00

Refactor msp to modules (#3214)

* feat: refactor everything to modules

* fix: a lot of circular deps fixes

* feat: use vitest instead of karma
This commit is contained in:
Tomas Chmelevskij 2023-01-14 22:11:37 +01:00 committed by GitHub
parent 654dca2a19
commit c086395def
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
82 changed files with 1660 additions and 1376 deletions

View file

@ -1,279 +0,0 @@
class MockAnalytics {
EVENT_CATEGORIES = {};
sendEvent() {
// Empty
}
}
let tracking;
describe('TABS.cli', () => {
function toArrayBuffer(string) {
const bufferOut = new ArrayBuffer(string.length);
const bufView = new Uint8Array(bufferOut);
for (let i = 0; i < string.length; i++) {
bufView[i] = string.charCodeAt(i);
}
return bufferOut;
}
describe('output', () => {
const cliTab = $('<div>').addClass('tab-cli');
const cliOutput = $('<div>').addClass('wrapper');
const cliPrompt = $('<textarea name="commands">');
cliTab.append($('<div>').addClass('window').append(cliOutput));
cliTab.append(cliPrompt);
CliAutoComplete.setEnabled(false); // not testing the client-side autocomplete
before(() => {
tracking = new MockAnalytics();
$('body').append(cliTab);
CONFIGURATOR.cliValid = true;
TABS.cli.GUI.windowWrapper = cliOutput;
});
after(() => cliTab.remove());
beforeEach(() => {
cliOutput.empty();
cliPrompt.val('');
TABS.cli.cliBuffer = "";
});
it('ambiguous auto-complete results', () => {
TABS.cli.cliBuffer = 'se';
TABS.cli.read({
data: toArrayBuffer('\r\033[Kserialpassthrough\tservo\r\n# ser'),
});
// Ambigous auto-complete from firmware is preceded with an \r carriage return
// which only renders a line break on Mac
const expectedValue = GUI.operating_system !== "Windows" ?
'se<br>serialpassthrough\tservo<br>' :
'seserialpassthrough\tservo<br>';
expect(cliOutput.html()).to.equal(expectedValue);
expect(cliPrompt.val()).to.equal('ser');
});
it('unambiguous auto-complete result', () => {
TABS.cli.read({
data: toArrayBuffer('serialpassthrough'),
});
expect(cliOutput.html()).to.equal('');
expect(cliPrompt.val()).to.equal('serialpassthrough');
});
it('unambiguous auto-complete result with partial buffer', () => {
TABS.cli.cliBuffer = 'serial';
TABS.cli.read({
data: toArrayBuffer('passthrough'),
});
expect(cliOutput.html()).to.equal('');
expect(cliPrompt.val()).to.equal('serialpassthrough');
});
it("escape characters are skipped", () => {
TABS.cli.read({
data: toArrayBuffer('\033[K'),
});
expect(cliOutput.html()).to.equal('');
expect(cliPrompt.val()).to.equal('');
});
});
function triggerEnterKey(input) {
const enterKeycode = 13;
const event = $.Event("keypress");
event.which = enterKeycode;
input.trigger(event);
}
function triggerTabKey(input) {
const tabKeycode = 9;
const event = $.Event("keydown");
event.which = tabKeycode;
input.trigger(event);
}
const backspaceCode = String.fromCharCode(127);
describe('input', () => {
const content = $('<div>').attr('id', 'content');
const cliTab = $('<div>').addClass('tab-cli');
const cliPrompt = $('<textarea name="commands">');
cliTab.append(cliPrompt);
beforeEach(() => {
$('body')
.append(content);
// Stub loading of template.
sinon.stub($.fn, 'load').callsFake((file, callback) => {
content.append(cliTab);
callback();
});
sinon.stub(TABS.cli, 'send');
sinon.stub(Promise, 'reduce').callsFake((items, cb) => {
items.forEach((line, idx) => cb(0, line, idx));
});
sinon.stub(window, 'Promise').callsFake(resolve => resolve(0));
sinon.stub(GUI, 'timeout_add').withArgs('CLI_send_slowly')
.callsFake((name, cb) => {
cb();
});
TABS.cli.cliBuffer = "";
});
afterEach(() => {
content.remove();
$.fn.load.restore();
TABS.cli.send.restore();
Promise.reduce.restore();
Promise.restore();
GUI.timeout_add.restore();
});
beforeEach(() => {
cliPrompt.val('');
content.empty();
});
it('tab key triggers serial message with appended tab char', done => {
TABS.cli.initialize(() => {
cliPrompt.val('serial');
triggerTabKey(cliPrompt);
expect(TABS.cli.send).to.have.been.calledOnce;
expect(TABS.cli.send).to.have.been.calledWith('serial\t');
done();
});
});
it('second auto complete in row', done => {
TABS.cli.initialize(() => {
TABS.cli.cliBuffer = '# ser';
cliPrompt.val('seri');
triggerTabKey(cliPrompt);
expect(TABS.cli.send).to.have.been.calledOnce;
expect(TABS.cli.send).to.have.been.calledWith('i\t');
done();
});
});
it('auto-complete command with trailing space', done => {
TABS.cli.initialize(() => {
TABS.cli.cliBuffer = '# get ';
cliPrompt.val('get r');
triggerTabKey(cliPrompt);
expect(TABS.cli.send).to.have.been.calledOnce;
expect(TABS.cli.send).to.have.been.calledWith('r\t');
done();
});
});
it('auto-complete after delete characters', done => {
TABS.cli.initialize(() => {
TABS.cli.cliBuffer = '# serial';
cliPrompt.val('ser');
triggerTabKey(cliPrompt);
expect(TABS.cli.send).to.have.been.calledOnce;
expect(TABS.cli.send).to.have.been.calledWith(backspaceCode.repeat(3) + '\t');
done();
});
});
it('enter after autocomplete', done => {
TABS.cli.initialize(() => {
TABS.cli.cliBuffer = '# servo';
cliPrompt.val('servo');
triggerEnterKey(cliPrompt);
expect(TABS.cli.send).to.have.been.calledOnce;
expect(TABS.cli.send).to.have.been.calledWith('\n');
done();
});
});
it('enter after autocomplete', done => {
TABS.cli.initialize(() => {
TABS.cli.cliBuffer = '# ser';
cliPrompt.val('servo');
triggerEnterKey(cliPrompt);
expect(TABS.cli.send).to.have.been.calledOnce;
expect(TABS.cli.send).to.have.been.calledWith('vo\n');
done();
});
});
it('enter after deleting characters', done => {
TABS.cli.initialize(() => {
TABS.cli.cliBuffer = '# serial';
cliPrompt.val('ser');
triggerEnterKey(cliPrompt);
expect(TABS.cli.send).to.have.been.calledOnce;
expect(TABS.cli.send).to.have.been.calledWith(backspaceCode.repeat(3) + '\n');
done();
});
});
it('cliBuffer is cleared on startup', done => {
TABS.cli.cliBuffer = '# serial';
TABS.cli.initialize(() => {
expect(TABS.cli.cliBuffer).to.equal('');
done();
});
});
it('exit upon cleanup clears cliBuffer first', done => {
CONFIGURATOR.connectionValid = true;
TABS.cli.cliValid = true;
TABS.cli.initialize(() => {
const commandInBuffer = 'resource';
TABS.cli.cliBuffer = `# ${commandInBuffer}`;
TABS.cli.cleanup();
expect(TABS.cli.send).to.have.been.calledOnce;
expect(TABS.cli.send).to.have.been.calledWith(backspaceCode.repeat(commandInBuffer.length) + 'exit\r');
done();
});
});
});
});

303
test/tabs/cli.test.js Normal file
View file

@ -0,0 +1,303 @@
import { Promise } from "bluebird";
import {
describe,
it,
expect,
beforeAll,
afterAll,
afterEach,
beforeEach,
vi,
} from "vitest";
import CliAutoComplete from "../../src/js/CliAutoComplete";
import { cli } from "../../src/js/tabs/cli";
import "jquery-textcomplete";
import $ from "jquery";
import CONFIGURATOR from "../../src/js/data_storage";
import GUI from "../../src/js/gui";
class MockAnalytics {
sendEvent() {
// Empty
}
}
MockAnalytics.prototype.EVENT_CATEGORIES = {};
function toArrayBuffer(string) {
const bufferOut = new ArrayBuffer(string.length);
const bufView = new Uint8Array(bufferOut);
for (let i = 0; i < string.length; i++) {
bufView[i] = string.charCodeAt(i);
}
return bufferOut;
}
function triggerEnterKey(input) {
const enterKeycode = 13;
const event = $.Event("keypress");
event.which = enterKeycode;
input.trigger(event);
}
function triggerTabKey(input) {
const tabKeycode = 9;
const event = $.Event("keydown");
event.which = tabKeycode;
input.trigger(event);
}
const backspaceCode = String.fromCharCode(127);
beforeAll(() => {
window.tracking = new MockAnalytics();
});
describe("cli", () => {
describe("output", () => {
let cliTab;
let cliOutput;
let cliPrompt;
beforeAll(() => {
cliTab = $("<div>").addClass("tab-cli");
cliOutput = $("<div>").addClass("wrapper");
cliPrompt = $('<textarea name="commands">');
cliTab.append($("<div>").addClass("window").append(cliOutput));
cliTab.append(cliPrompt);
CliAutoComplete.setEnabled(false); // not testing the client-side autocomplete
$("body").append(cliTab);
CONFIGURATOR.cliValid = true;
cli.GUI.windowWrapper = cliOutput;
});
afterAll(() => {
cliTab.remove();
});
beforeEach(() => {
cliOutput.empty();
cliPrompt.val("");
cli.cliBuffer = "";
});
it("ambiguous auto-complete results", () => {
cli.cliBuffer = "se";
cli.read({
data: toArrayBuffer(
"\r\x1B[Kserialpassthrough\tservo\r\n# ser"
),
});
// Ambigous auto-complete from firmware is preceded with an \r carriage return
// which only renders a line break on Mac
const expectedValue =
GUI.operating_system !== "Windows"
? "se<br>serialpassthrough\tservo<br>"
: "seserialpassthrough\tservo<br>";
expect(cliOutput.html()).toEqual(expectedValue);
expect(cliPrompt.val()).toEqual("ser");
});
it("unambiguous auto-complete result", () => {
cli.read({
data: toArrayBuffer("serialpassthrough"),
});
expect(cliOutput.html()).toEqual("");
expect(cliPrompt.val()).toEqual("serialpassthrough");
});
it("unambiguous auto-complete result with partial buffer", () => {
cli.cliBuffer = "serial";
cli.read({
data: toArrayBuffer("passthrough"),
});
expect(cliOutput.html()).toEqual("");
expect(cliPrompt.val()).toEqual("serialpassthrough");
});
it("escape characters are skipped", () => {
cli.read({
data: toArrayBuffer("\x1B[K"),
});
expect(cliOutput.html()).toEqual("");
expect(cliPrompt.val()).toEqual("");
});
});
describe("input", () => {
let content;
let cliTab;
let cliPrompt;
beforeAll(() => {
content = $("<div>").attr("id", "content");
cliTab = $("<div>").addClass("tab-cli");
cliPrompt = $('<textarea name="commands">');
cliTab.append(cliPrompt);
$("body").append(content);
vi.spyOn($.fn, "load").mockImplementation((file, callback) => {
content.append(cliTab);
// callback();
});
vi.mock("../src/js/tabs/cli", async (importOriginal) => {
const mod = await importOriginal();
return {
...mod,
send: () => {},
};
});
vi.spyOn(Promise, "reduce").mockImplementation((items, cb) => {
items.forEach((line, idx) => cb(0, line, idx));
});
vi.spyOn(Promise, "Promise").mockResolvedValue(0);
vi.spyOn(GUI, "timeout_add").mockImplementation((name, cb) => {
cb();
});
cli.cliBuffer = "";
});
afterEach(() => {
content.remove();
vi.resetAllMocks();
});
beforeEach(() => {
cliPrompt.val("");
content.empty("");
});
it("tab key triggers serial message with appended tab char", (done) => {
cli.initialize(() => {
cliPrompt.val("serial");
triggerTabKey(cliPrompt);
expect(cli.send).toHaveBeenCalledOnce();
expect(cli.send).toHaveBeenCalledWith("serial\t");
done();
});
});
it("second auto complete in row", (done) => {
cli.initialize(() => {
cli.cliBuffer = "# ser";
cliPrompt.val("seri");
triggerTabKey(cliPrompt);
expect(cli.send).toHaveBeenCalledOnce();
expect(cli.send).toHaveBeenCalledWith("i\t");
done();
});
});
it("auto-complete command with trailing space", (done) => {
cli.initialize(() => {
cli.cliBuffer = "# get ";
cliPrompt.val("get r");
triggerTabKey(cliPrompt);
expect(cli.send).toHaveBeenCalledOnce();
expect(cli.send).toHaveBeenCalledWith("r\t");
done();
});
});
it("auto-complete after delete characters", (done) => {
cli.initialize(() => {
cli.cliBuffer = "# serial";
cliPrompt.val("ser");
triggerTabKey(cliPrompt);
expect(cli.send).toHaveBeenCalledOnce();
expect(cli.send).toHaveBeenCalledWith(
`${backspaceCode.repeat(3)}\t`
);
done();
});
});
it("enter after autocomplete", (done) => {
cli.initialize(() => {
cli.cliBuffer = "# servo";
cliPrompt.val("servo");
triggerEnterKey(cliPrompt);
expect(cli.send).toHaveBeenCalledOnce();
expect(cli.send).toHaveBeenCalledWith("\n");
done();
});
});
it("enter after autocomplete", (done) => {
cli.initialize(() => {
cli.cliBuffer = "# ser";
cliPrompt.val("servo");
triggerEnterKey(cliPrompt);
expect(cli.send).toHaveBeenCalledOnce();
expect(cli.send).toHaveBeenCalledWith("vo\n");
done();
});
});
it("enter after deleting characters", (done) => {
cli.initialize(() => {
cli.cliBuffer = "# serial";
cliPrompt.val("ser");
triggerEnterKey(cliPrompt);
expect(cli.send).toHaveBeenCalledOnce();
expect(cli.send).toHaveBeenCalledWith(
`${backspaceCode.repeat(3)}\n`
);
done();
});
});
it("cliBuffer is cleared on startup", (done) => {
cli.cliBuffer = "# serial";
cli.initialize(() => {
expect(cli.cliBuffer).to.equal("");
done();
});
});
it("exit upon cleanup clears cliBuffer first", (done) => {
CONFIGURATOR.connectionValid = true;
cli.cliValid = true;
cli.initialize(() => {
const commandInBuffer = "resource";
cli.cliBuffer = `# ${commandInBuffer}`;
cli.cleanup();
expect(cli.send).toHaveBeenCalledOnce();
expect(cli.send).toHaveBeenCalledWith(
`${backspaceCode.repeat(commandInBuffer.length)}exit\r`,
);
done();
});
});
});
});