1.5.11 • Published 8 years ago

lc2 v1.5.11

Weekly downloads
2
License
GPL-3.0
Repository
github
Last release
8 years ago

http://lemonce.github.io/lemoncase2

#lemoncase2

It's a tiny language for writing tests.

Build Status

Install

npm install --save lc2

Grammar

All statements must end with a semicolon.
example
click 'here';

Identifier

  • Similar to JavaScript
    var a = 1;
  • You do not have to use 'var' keyword (there's no type check currently, haha)
    a = 1; a = true;

Datatype

Below is a list of data type you can use

  • number (e.g., 0.12, 1e-3)
  • string (e.g., 'you', "me")
  • Boolean (true/false)
  • RegExp

RegExp will be used as it is, to match against a string, if it is used with ~~ operator.
Otherwise it will generate a random string based on the regexp pattern (use randexp underneath).
Use it if you want to do some fuzzy testing.

Macro

Macro is the global setting for your case during runtime.

#TIMES times:number|1

// The case will run 10 times.
#TIMES 10

#INTERVAL interval:number|3000

// The case will wait 1000ms before start another one.
#INTERVAL 1000

#CLOCK clock:number>5|10

// The frequency of the engine, the engine will tick per 30ms.
#CLOCK 30

#SCREEN width:number,height:number

// Set the screen to width 800px and height 600px.
#SCREEN 800,600

#AUTOSCROLL

// Auto scroll the window when trigger an instruction.
#AUTOSCROLL

Instruction keyword

action by/to params|params1,params2

Simulate an action

assert exp in overtime:number]

Assert that the expression is true within overtime.

wait time:number

Wait a few time like a person.

log exp

Record the value of an expression.

jumpto exp

Jump to a URL.

refresh

Refresh the page.

process identifier { statements }

Define a test process.

Action keyword

You can not dispatch any event to an <iframe> element, so your instruction will not take effect on those elements.

Any error occurred during execution will be recorded in the log.
For example, if your target element does not exist, or you can't dispatch any action to target element.

input

Type a string into an element like <input type="text" /> or <textarea>.

// <input id="link" class="case" />
input 'div#link.case' by "hello world.";

click

Click a HTML element.

// <a href="...">Text</a>
click 'a';

// <a id="link" href="...">Text</a>
click 'a#link';

dblclick

Double click a HTML element.

// <div id="link" class="case">Text</div>
dblclick 'div#link.case';

rclick

Right click a HTML element.

// <div id="link" class="case">Text</div>
rclick 'div#link.case';

select selector:string by value:string|index:number

Select a <option> in <select> by value|index. (only works on <select> element)

// <select>
//   <option value="a"></option> // index:0
//   <option value="b"></option> // index:1 <-- this will be selected
//   <option value="c"></option> // index:2
// </select>

select "select" by 1;

// OR

select "select" by "b";

movein - beta

Trigger move enter events on a HTML element.

moveout - beta

Trigger move leave events on a HTML element.

scroll - beta

Scroll to a specific position or element on page.

Expression

  • Expression is what you will expect in any c-like language

Extended selector

When using a selector, you can use a string that is compatible with the following

  • Native querySelectorAll.
  • jQuery Sizzle ^2.3.0.
    • It means you can use some useful pseudo class like :contain().
  • iframe selector < that select elements inside <iframe>*
<!-- From top frame -->
<iframe id="top">
    #document
    <html>
        <body>
            <iframe id="project">
                #document
                <html>
                    <body>
                        <a href="./">link</a> <!-- select it. -->
                        <b>link</b>
                    </body>
                </html>
            </iframe>
        </body>
    </html>
</iframe>
  • iframe < iframe < a
  • * < * < a
  • #top < #project < a
  • * < #project < a:contain(link)

Selector operator

<# [selector:string] />

// Get the number of elements that matches the selector.
// <div id="link" class="case">Text</div>

log <# 'div'/>; // Output 1
log <# '#link'/>; // Output 1
log <# '#no'/>; // Output 0

<@ [selector:string] />

// Get the innerHTML of the 1st element that matches the selector.
// The result is a string or `false` if there's no match.

// <div id="link" class="case">Text</div>

log <@ 'div'/>; // Output Text
log <@ '#link'/>; // Output Text
log <@ '#no'/>; // Output false

<! [selector:string] />

// Check for element visibility.
// The result is `true` if such element exist && width > 0 && height > 0.
// otherwise it is false

// <div id="link" class="case">Text</div>
// <div id="link2" style="display:none">Text</div>

log <@ 'a'/>; // Output false
log <@ 'div'/>; // Output true
log <@ '#link2'/>; // Output false

Compare operator

[exp_A:string] ~~ [exp_B:string|RegExp]

  • exp_B is not a string or RegExp, false.
  • exp_A is not a string, false.
  • exp_A is not matched to exp_B(RegExp), false.
  • exp_A has a sub string like exp_B, true.
  • exp_A is matched to exp_B(RegExp), true.

[exp_A:string] !~ [exp_B:string|RegExp]

  • !(exp_A ~~ exp_B).

filter operator

Filter a expression, usually a string.
exp|filter(arg1, arg2, ...)[|filter(arg1, arg2, ...)...]

example:

"string" | substr(1, -1) | slice(0, 2);

Scope

  • Global scope. So there is no return keyword in LC2. A process has no parameter.
  • process main is the program's entry.

Example

Here are some examples for you to get started with the language.

Hello, world

process main {
    log 'hello world';
}

A typical test case

A test case that runs only once.

#TIMES 1

process main {
    jumpto '[URL]';
    wait 2000;
    ...
    [statement]
    ...
}

Typical examples

Test a user registration page.

#TIMES 1
#INTERVAL 3000

process main {
	jumpto '/#/register';
	wait 2000;
	// There are 5 text boxes.
	assert <#'#userInfoFrom input'/> === 5;
	// There is a captcha svg.
	assert <!"img.pull-right"/>;
	// There is a login button.
	assert <!"#userInfoFrom > button.btn"/>;
	// There is a home button.
	assert <@"#global-back-button"/> ~~ 'glyphicon-home';

	userNameRule = "#name-rule > div";

	// Input username length < 6
	input "#account-name" by '12345';
	assert <@userNameRule/> ~~ '不少于6个字符';

    // Input username length > 16
	input "#account-name" by /\w{17}/;
	assert <@userNameRule/> ~~ '不超过16个字符';

	// Username is blank
	input "#account-name" by '';
	assert <@userNameRule/> ~~ '用户名不为空';

	// Username is repeat.
	input "#account-name" by 'active';
	click "#account-email";
	assert <!"#name-repeat"/> && <@"#name-repeat"/> ~~ '用户名重复' in 2000;
	
	// Blank email.
	emailRule = "#email-rule > div";
	input "#account-email" by '';
	assert <@emailRule/> ~~ '邮箱不为空';

	// Error email
	input "#account-email" by 'fdsf';
	assert <@emailRule/> ~~ '请输入正确格式的邮箱';

	// Repeat email
	input "#account-email" by 'lichao@or-change.cn';
	click "#account-password";
	assert <!"#emial-repeat"/> && <@"#emial-repeat"/> ~~ '邮箱重复' in 2000;
	
	// Input password length < 8
	input "#account-password" by /\w{1,7}/;
	assert <@"#password-rule > div"/> ~~ '不少于8个字符';
	// Input password length >20
	input "#account-password" by /\w{20,}/;
	assert <@"#password-rule > div"/> ~~ '不超过20个字符';
	// Blank password
	input "#account-password" by '';
	assert <@"#password-rule > div"/> ~~ '密码不为空';
	// Error char.
	input "#account-password" by '测试测试测试测试';
	assert <@"#password-rule > div"/> ~~ '您输入了非法字符';
	
	// Password !== Confirm
	password = /\w{10}/;
	input "#account-password" by password;
	input "#account-password-confirm" by password + 'a';
	assert <@"#confirm-rule > div"/> ~~ '两次输入不一致';
	
}
1.5.11

8 years ago

1.5.10

8 years ago

1.5.9

8 years ago

1.5.8

8 years ago

1.5.7

8 years ago

1.5.6

8 years ago

1.5.5

8 years ago

1.5.4

8 years ago

1.5.3

8 years ago

1.5.2

8 years ago

1.5.1

8 years ago

1.5.0

8 years ago

1.4.8

8 years ago

1.4.7

8 years ago

1.4.6

8 years ago

1.4.5

8 years ago

1.4.4

8 years ago

1.4.3

8 years ago

1.4.2

8 years ago

1.4.1

8 years ago

1.4.0

8 years ago

1.3.5

8 years ago

1.3.4

8 years ago

1.3.3

8 years ago

1.3.2

8 years ago

1.3.1

8 years ago

1.3.0

8 years ago

1.2.3

8 years ago

1.2.1

8 years ago

1.2.0

8 years ago

1.1.3

8 years ago

1.1.2

8 years ago

1.1.0

8 years ago